__slots__
问题的引出
都是字典惹的祸。
字典为了提升查询效率,必须用空间换时间。
一般来说一个对象,属性多一点,都存储在字典中便于查询,问题不大。
但是如果数百万个对象,那么字典占的就有点大了。
这个时候,能不能把属性字典__dict__
省了?
Python提供了__slots__
class A:
X = 1
def __init__(self):
self.x = 5
self.y = 6
def show(self):
print(self.X, self.y, self,z)
a = A()
print(1, A.__dict__)
print(2, a.__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~
1 {'__init__': <function A.__init__ at 0x000001F2327E19D8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'X': 1, 'show': <function A.show at 0x000001F2327E1A60>, '__module__': '__main__'}
2 {'x': 5, 'y': 6}
思考:
上面2个字典,谁的字典是个问题?
实例多大百万个的时候,这么多存放实例属性的字典是个问题
class A:
X = 1
__slots__ = ('y', 'z') # 元组
# __slots__ = ['y', 'z'] # 列表,可以吗
# __slots__ = 'y', 'z'
# __slots__ = 'y' # AttributeError: 'A' object has no attribute 'z'
def __init__(self):
self.y = 5
self.z = 6
def show(self):
print(self.X, self.y)
a = A()
a.show()
print(1, 'A', A.__dict__)
# print(2, 'obj', a.__dict__)
print(3, a.__slots__)
__slots__
告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是用元组比较好。
一旦类提供了slots,就阻止实例产生__dict__
来保存实例的属性。
尝试为实例a动态增加属性
a.newx = 5
返回AttributeError: ‘A’ object has no attribute ‘newx’
说明实例不可以动态增加属性了
A.NEWX = 20,这个是可以的,因为这个类属性。
继承
class A:
X = 1
__slots__ = ('y', 'z') # 元组
def __init__(self):
self.y = 5
self.z = 6
def show(self):
print(self.X, self.y)
a = A()
a.show()
print(1, 'A', A.__dict__)
# print(2, 'obj', a.__dict__)
print(3, a.__slots__)
class B(A):
pass
print(4, 'B', B().__dict__)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 5
1 A {'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x000001FC56B719D8>, 'X': 1, '__doc__': None, '__module__': '__main__', 'show': <function A.show at 0x000001FC56B71A60>}
3 ('y', 'z')
4 B {}
__slots__
不影响子类实例,不会继承下去,除非子类里面自己也定义了__slots__
应用场景
使用需要构建在数百万以上对象,且内存容量较为紧张,实例的书信更简单、固定不用动态增加的场景。
未实现和未实现异常
print(type(NotImplemented))
print(type(NotImplementedError))
# raise NotImplemented # TypeError: exceptions must derive from BaseException
raise NotImplementedError
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<class 'NotImplementedType'>
<class 'type'>
Traceback (most recent call last):
File "C:/python10/code/Python10/test.py", line 5, in <module>
raise NotImplementedError
NotImplementedError
NotImplemented是个值,单值,是NotImplementedType类的实例
NotImplementedError是类型,是异常,返回type
运算符重载中的反向方法
前面学习过运算符重载的方法,例如add和iadd
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
a = A(4)
b = A(5)
print(1, a, b)
print(2, a + b)
print(3, b + a)
b += a
a += b
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 <__main__.A object at 0x0000029198F8A278> <__main__.A object at 0x0000029198F8A3C8>
<__main__.A object at 0x0000029198F8A278> add
2 9
<__main__.A object at 0x0000029198F8A3C8> add
3 9
<__main__.A object at 0x0000029198F8A3C8> iadd
<__main__.A object at 0x0000029198F8A278> iadd
__radd__
方法根本没有执行过,为什么?
因为都是A的实例,都是调用的__add__
,无非就是实例a还是b调用而已。
测试一下a + 1
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
a = A(4)
a + 1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Traceback (most recent call last):
<__main__.A object at 0x0000029688482320> add
File "C:/python10/code/Python10/test.py", line 19, in <module>
a + 1
File "C:/python10/code/Python10/test.py", line 7, in __add__
return self.x + other.x
AttributeError: 'int' object has no attribute 'x'
出现了AttributeError,因为1是int类型,没有x这个属性,还是__add__
被执行了。
测试1 + a,运行结果如下
<__main__.A object at 0x00000210BE3C2320> radd
Traceback (most recent call last):
File "C:/python10/code/Python10/test.py", line 19, in <module>
1 + a
File "C:/python10/code/Python10/test.py", line 15, in __radd__
return self.x + other.x
AttributeError: 'int' object has no attribute 'x'
这次执行的是实例a的__radd__
方法。
1 + a等价于1.__add__(a)
,而int类型实现了__add__
方法的,为什么缺不抛出异常,而是执行了实例a的__radd__
方法?
再看一个例子
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
class B: # 未实现__add__
def __init__(self, x):
self.x = x
a = A(4)
b = B(10)
print(a + b)
print(b + a)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<__main__.A object at 0x0000021138F4A278> add
14
<__main__.A object at 0x0000021138F4A278> radd
14
b + a等价于b.__add__(a)
,但是类B没有实现__add__
方法,就去找a的__radd__
方法
1 + a等价于1.__add__(a)
,而int类型实现了__add__
方法的,不过这个方法对于这种加法的返回值是NotImplemented,解释器发现是这个值,就会发起对第二操作对象__radd__
方法的调用。
1 + a能解决吗?
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
try:
x = other.x
return self.x + other.x
except AttributeError:
try:
x = int(other)
except:
x = 0
return self.x + x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self + other
class B:
def __init__(self, x):
self.x = x
a = A(4)
b = B(10)
print(1, a + b)
print(2, b + a)
print(3, a + 2)
print(4, 2 + a)
print(5, a + 'abc')
print(6, 'abc' + a)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<__main__.A object at 0x000001E3019EA7F0> add
1 14
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
2 14
<__main__.A object at 0x000001E3019EA7F0> add
3 6
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
4 6
<__main__.A object at 0x000001E3019EA7F0> add
5 4
<__main__.A object at 0x000001E3019EA7F0> radd
<__main__.A object at 0x000001E3019EA7F0> add
6 4
‘abc’ + a, 字符串也实现了__add__
方法,不过默认是处理不了和其他类型的加法,就返回NotImplemented.