1. __slots__的作用
在Python中,类的实例可以动态地添加属性,这在某些情况下很方便,但在其他情况下,这可能是不必要的,甚至是危险的。Python提供了一种机制,即使用__slots__属性限制实例动态属性的创建。使用__slots__可以极大地减少内存的使用,提高类的运行效率。
2. __slots__的语法
在Python中,__slots__是一个特殊的类属性,它是一个由字符串组成的序列,其中每个字符串表示一个实例变量的名称。
class MyClass:
__slots__ = ['x', 'y']
上面这个例子中,MyClass类使用__slots__属性限制了它的实例可以拥有的属性只能是'x'和'y',如果试图在实例中创建其他属性,就会抛出AttributeError异常。
3. __slots__的特性
3.1 减少内存占用
在没有使用__slots__属性的情况下,每个实例对象都会有一个__dict__属性,用于存储实例的属性名和对应的值。__dict__是一个字典,会占用相当多的内存,而使用__slots__属性可以避免这个问题。
下面这个例子,使用__slots__属性和不使用__slots__属性的内存占用情况进行对比。
import sys
class A:
pass
class B:
__slots__ = ['x', 'y']
a = A()
b = B()
print(sys.getsizeof(a)) # 输出98
print(sys.getsizeof(b)) # 输出56
在上面的例子中,使用了sys.getsizeof()函数,可以查看对象占用内存的大小。可以看出,使用__slots__属性之后,B类的实例比A类的实例少了42个字节。
3.2 提高运行速度
除了减少内存占用之外,使用__slots__属性还可以提高类的运行速度。这是由于在创建实例时,Python不再需要为__dict__属性分配内存。
4. 使用__slots__的注意事项
4.1 如果定义了父类,子类也要定义__slots__
如果子类没有定义__slots__属性,那么它的实例将拥有父类中定义的所有实例变量以及__dict__属性。如果为了减少内存占用和提高运行速度而使用了__slots__属性,这样就会失去它的作用。
class A:
__slots__ = ['x', 'y']
class B(A):
pass
b = B()
b.z = 1 # 不会引发AttributeError异常
在上面的例子中,因为B类没有定义__slots__属性,所以可以在它的实例中添加一个z属性,这就打破了原来使用__slots__的限制。
因此,如果定义了父类,子类也要定义__slots__属性。
4.2 __slots__属性中不要包含__dict__属性
在使用__slots__属性时,可以指定实例变量的名称,但不能包含__dict__属性。如果包含了__dict__属性,在实例中创建任何属性都会抛出AttributeError异常。
class MyClass:
__slots__ = ['__dict__'] # 包含__dict__属性
mc = MyClass()
mc.x = 1 # 抛出AttributeError异常
在上面的例子中,因为MyClass类的__slots__属性中包含了__dict__属性,所以在实例中创建任何属性都会抛出AttributeError异常。
5. 总结
使用__slots__属性可以减少内存的占用和提高类的运行速度,但使用__slots__属性也有注意事项。在定义父类和子类时,子类也应该定义__slots__属性,而且在__slots__属性中不应该包含__dict__属性。