目录
7.1 如何派生内置不可变类型并修改实例化行为
object.
__new__
(cls[, ...])
__new_()
是在对象的实例化中被调用的第一个方法, 是一个静态方法,不需要显式地声明。它会将类作为第一个参数,其余参数传递给对象构造器表达式,返回值为新对象实例 。
示例:派生元祖类,定义一个元素仅为 int 类型的新元组。
#在 __new_()中对数据进行过滤
>>> class IntTuple(tuple):
def __new__(cls,iterable):
g = (i for i in iterable if isinstance(i,int))
return super().__new__(cls,g)
def __init__(self,iterable):
super().__init__()
>>> l
[1, 2, 'gyf', 45]
>>> a = IntTuple(l)
>>> a
(1, 2, 45)
7.2 如何为创建大量实例节省内存
给类添加 __slots__
属性
演示对比:
>>> class Customer:
def __init__(self,id,name,level):
self.id = id
self.name = name
self.level = level
'''使用了 __slots__ 属性
'''
>>> class New_customer:
__slots__ = ['id','name','level']
def __init__(self,id,name,level):
self.id = id
self.name = name
self.level = level
两个类的示例差异:
>>> c = Customer(7,'gyf',100)
>>> c_n = New_customer(7,'gyf',100)
>>> set(dir(c)) - set(dir(c_n))
{'__weakref__', '__dict__'}
>>> import sys
>>> sys.getsizeof(c.__dict__)
112
>>> sys.getsizeof(c.__weakref__)
16
可见使用了 __slots__ ,示例少了两个属性'__dict__', '__weakref__' ,从而节省了内存。使用slots一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在 __slots__
中定义的那些属性名。
object.
__dict__
一个字典或其他类型的映射对象,用于存储对象的(可写)属性。
__dict__是一个dict,它和数据对象的属性直接关联,可以直接通过__dict__访问、设置、修改、删除属性,比如类的对象实例可以通过 self.x=3
设置x属性,也可以通过 __dict__['x']=3
来设置属性x。而dir()函数仅仅只是展现一些属性。
7.3 如何让对象支持上下文管理器
给类添加 __enter__() 和 __exit__() 方法
7.4 如何创建可管理的对象属性
直接访问对象的属性可能是不安全的,或者是设计上不灵活。比如 Customer类的 _level 属性只能是 1~4,直接访问属性就会没办法对此进行限制。
改进为调用方法的形式来访问属性:
>>> class Customer_new:
def __init__(self,id,name):
self.id = id
self.name = name
def getlevel(self):
return self._level
def setlevel(self,_level):
if _level not in (1,2,3,4):
raise ValueError('Level must be 1~4')
self._level = _level
def dellevel(self):
del self._level
>>> gyf = Customer_new(7,'gyf')
>>> gyf.setlevel(3)
>>> gyf.getlevel()
3
>>> gyf.dellevel()
>>> gyf.getlevel() #属性删除了
Traceback (most recent call last):
File "<pyshell#127>", line 1, in <module>
gyf.getlevel()
File "<pyshell#119>", line 8, in getlevel
return self._level
AttributeError: 'Customer_new' object has no attribute '_level'
调用方法的形式来访问属性虽然解决了直接访问的问题,但是会使访问变得不简洁。
改进:
class property
(fget=None, fset=None, fdel=None, doc=None)¶
返回 property 属性。
fget 是获取属性值的函数。 fset 是用于设置属性值的函数。 fdel 是用于删除属性值的函数。并且 doc 为属性对象创建文档字符串。
>>> class Customer_new:
def __init__(self,id,name):
self.id = id
self.name = name
def getlevel(self):
return self._level
def setlevel(self,_level):
if _level not in (1,2,3,4):
raise ValueError('Level must be 1~4')
self._level = _level
def dellevel(self):
del self._level
level = property(getlevel,setlevel,dellevel)
>>> gyf = Customer_new(7,'gyf')
>>>
>>> gyf.level = 3
>>> gyf.level
3
>>> del gyf.level
>>> gyf.level
Traceback (most recent call last):
File "<pyshell#166>", line 1, in <module>
gyf.level
File "<pyshell#159>", line 7, in getlevel
return self._level
AttributeError: 'Customer_new' object has no attribute '_level'
>>>
或者使用 @property
装饰器
>>> class Customer_new:
def __init__(self,id,name):
self.id = id
self.name = name
@property
def level(self):
return self._level
@level.setter
def level(self,_level):
if _level not in (1,2,3,4):
raise ValueError('Level must be 1~4')
self._level = _level
@level.deleter
def level(self):
del self._level
>>> gyf = Customer_new(7,'gyf')
>>>
>>> gyf.level #还没level属性
Traceback (most recent call last):
File "<pyshell#176>", line 1, in <module>
gyf.level
File "<pyshell#173>", line 8, in level
return self._level
AttributeError: 'Customer_new' object has no attribute '_level'
>>> gyf.level = 3
>>> gyf.level
3
>>> del gyf.level
@property
会将 level 方法转化为 level.getter方法,@level.setter 和 @level.deleter 则把 level 方法转化为 level.setter 和 level.deleter 方法
7.5 如何让类支持比较操作
__lt__ , __le__ , __gt__ , __ge__ ,__eq__ , __ne__
或者 functools 模块的 total.ordering 装饰器
7.6 如何使用描述符对实例属性做类型检查
__get__ , __set__ , __delete__