python作为一门动态语言,可以在对象创建后动态的添加属性和方法。
示例1:动态添加属性
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
dog = Dog('ahuang', 10)
dog.home = 'China' # 直接外部定义
print(dog.home)
示例2:动态添加属性
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
def love(self, value): # 定义外部函数
self.favorite = value
print(f"The dog loves {value}")
from types import MethodType
dog.love = MethodType(love, dog) # 通过MethodType将外部函数绑定到对象上
print(dog.love) # >
print(dog.favorite) # 'meat'
动态语言的特性无疑增加了python语言的灵活性,但也带来了风险:暴露的对象可以被用户任意恶意添加属性。而类的__slots__属性提供了一种解决方案:
示例3:定义__slots__属性
class Dog_slot(object):
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
dog_slot = Dog_slot('ahuang', 10)
dog_slot.home = 'China' # 直接报错:AttributeError: 'Dog' object has no attribute 'home'
在上面的Dog
类中直接申明了所有的属性,因此无法定义其他的属性(无论是内部定义还是外部绑定!)
除了无法定义新的属性外,在定义__slots__后,还有一个额外的好处:节省内存空间,因为此时对象没有了__dict__属性,而会增加__slots__。因为__slots__是静态的,而非__dict__支持动态添加,因此对象会节省memory。
因此,当事先确定好class的所有attributes时候,建议使用slots来节省memory以及获得更快的attribute access。此外,不应当机械的为了防止创造__slots__之外的新属性作为使用__slots__的原因。
在__slots__的使用,还需要注意如下几点:
(1)父类定义了__slots__, 子类没定义__slots__,则子类会完全继承父类的__slots__以及__slots__中定义的属性。同时会生成__dict__属性,因此仍可以动态添加新的属性;
(2)父类定义了__slots__,子类中定义了新的__slots__,则子类会继承父类的__slots__中涉及的相关属性,而__slots__中只保留新定义的属性;
(3)父类定义了__slots__,子类中定义的__slots__与父类有重复,则作为重复项处理;
(4)如果为多继承关系,且父类的各__slots__(若定义了的话)不一致,则会报错。
【Reference】