- 1. Python 对象管理
- 1.1 类中的对象管理
- 1.2 类中的属性访问
- 1.3 类中的继承
- 2. 类和封装
- 2.1 共有与私有
- 2.2 @装饰器语法
- 2.3 Properties
Python 的对象系统是基于字典的实现。
字典是 Python 中最常使用的数据结构,也是最重要的数据类型。
在一个模块(Python 文件中),字典储存所有的全局变量和函数。
输入以下指令可以查看储存当前文件变量或者类属性的字典。
文件名.__dict__ 类名.__dict__ #或者 globals()1.1 类中的对象管理
Python 中的对象系统是在字典上的一个额外层。
假设有一个股票类,其属性值为 name, shares, price
实例一个对象,并查看储存类属性的字典:
>>> s = Stock('GOOG', 100, 490.1)
>>> s.__dict__
{'name' : 'GOOG', 'shares' : 100, 'price': 490.1 }
由于实例对象被分配在类的self 中,还可以以下方式查看。
self.__dict__
>>>{
'name': 'GOOG',
'shares': 100,
'price': 490.1
}
类字典
__class__#查看类字典
每一个实例对象的属性,储存在自己的私有字典中。
实例字典保存每个实例唯一的实例属性,而类字典保存所有实例共同共享的类属性。
class Foo(object):
a = 13 # Class variable
def __init__(self,b):
self.b = b # Instance variable
>>> f = Foo(10) >>> g = Foo(20) >>> f.a # Inspect the class variable (same for both instances) 13 >>> g.a 13 >>> f.b # Inspect the instance variable (differs) 10 >>> g.b 20 >>> Foo.a = 42 # Change the value of the class variable >>> f.a 42 >>> g.a 42 >>>1.2 类中的属性访问
Python 中一般使用 . 运算符访问属性与方法。
x = obj.name # Getting obj.name = value # Setting del obj.name # Deleting
实例的属性储存在两个地方:
- 本地实例字典
- 类字典
通常访问属性的顺序是先检查本地字典再检查类字典。
修改对象的操作会更新底层字典。
>>> s = Stock('GOOG', 100, 490.1)
>>> s.__dict__
{ 'name':'GOOG', 'shares': 100, 'price': 490.1 }
>>> s.shares = 50 # Setting
>>> s.date = '6/7/2007' # Setting
>>> s.__dict__
{ 'name': 'GOOG', 'shares': 50, 'price': 490.1, 'date': '6/7/2007' }
>>> del s.shares # Deleting
>>> s.__dict__
{ 'name': 'GOOG', 'price': 490.1, 'date': '6/7/2007' }
>>>
1.3 类中的继承
类可以继承其他类。
class A(B, C):
...
基类存储在子类的元组中。
>>> A.__bases__ (, ) >>>
从逻辑上讲,继承类查找属性的过程如下。
首先,检查本地__dict__. 如果没有找到,请查看类的__dict__。
如果在类中找不到,请通过 . 查看基类__bases__。
读取单一继承
class A: pass class B(A): pass class C(A): pass class D(B): pass class E(D): pass
Python 的方法解析顺序储存在 MRO中:
>>> E.__mro__ (, , , , ) >>>
读取多重继承
class A: pass class B: pass class C(A, B): pass class D(B): pass class E(C, D): pass
Python 多重继承读取规则:
-先读取子类
-再读取父类,(如果父类有多个)始终按列出的顺序进行读取。
>>> E.__mro__ (2. 类和封装, , , , , ) >>>
在编写类时,通常会尝试封装内部细节。
本节介绍了一些 Python 编程习惯,包括私有变量和属性。
2.1 共有与私有类的主要作用之一是封装对象的属性和实现内方法部细节。
然而,一个类也定义了一个与外部世界来操作对象的公共接口。
实现细节和公共接口之间的区别很重要。
在 Python 中,几乎所有关于类和对象的东西都是开放的。
- 可以轻松检查对象内部。
- 可以随意改变事物。
- 没有严格的访问控制概念(即私有类成员)。
任何带有前导的属性名称 _ 都被认为是私有的。
class Person(object):
def __init__(self, name):
self._name = 0
这只是一种编程风格,我们仍然可以访问和更改它。
>>> p = Person('Guido')
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
任何带有前导的名称 _ 都被认为是私有的,无论它是变量、函数还是模块名称。
2.2 @装饰器语法@ 被称为装饰器语法,对紧随其后的方法进行修饰。
使用__slots__可以限制属性名称。
class Person(object):
__slots__ = ('name')
def __init__(self, name):
self._name = 0
slot 最常被用作对数据结构类的优化,使用 slot 优化了 Python 使用内存的性能, 但在平常类的使用中应该避免。
2.3 Properties对类的属性赋值增加一些限制,例如以下代码 shares 应该是数值类型变量,不能用字符串类型赋值。
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
使用 properties 对属性增加限制,起到了保护私有属性的作用,使用者从外部不能轻易更改私有属性,但进一步想要更改私有属性,可使用 @shares.setter 可以对私有属性更改和增加限制。
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError('Expected int')
self._shares = value
访问 shares 会触发@property,shares 不会轻易被修改,从而保护了shares。
shares 被调用时与之前访问类属性一致。
修改 shares 会触发以上 @shares.setter 方法。
>>> s = Stock('IBM', 50, 91.1)
>>> s.shares # Triggers @property
50
>>> s.shares = 75 # Triggers @shares.setter
>>>
使用 @shares.setter 这种方法,不需要对源代码进行任何更改。
当类内有赋值时也会调用 setter,包括__init__() 内。
class Stock:
def __init__(self, name, shares, price):
...
# This assignment calls the setter below
self.shares = shares
...
...
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError('Expected int')
self._shares = value
尽管 property 中使用私有名称像 _shares,但类的其他部分可以继续使用 shares 名称。
如下,在 cost 方法中 使用 self.share 的方式对 shares 进行操作。
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def cost(self):
return self.shares * self.price
...
以上使用 @property 对 cost 方法进行限制,当 cost 被调用时,不需要加 ()。
>>> s = Stock('GOOG', 100, 490.1)
>>> s.shares # Instance variable
100
>>> s.cost # Computed Value
49010.0
>>>
不要过度使用私有属性、属性、slot 等。
它们用于特定目的,但是,对于大多数日常编码来说,它们并不是必需的。



