概述: Python中每个对象(类实例,类,函数,列表,字典等)都是拥有一个属于自己的命名空间的,其中存储着属于该对象自己的属性或者方法。一般来说,一个对象的命名空间中的数据可以用该对象的 __ dict __ 属性进行查看,如:
class Dog(object): def __init__(self,name): self._name = name dog = Dog("Alice") print(dog.__dict__) # {'_name': 'Alice'}
可以发现, __ dict __ 字典里保存着的是该实例对象的命名空间中的实例属性,我们可以将其成为“对象命名空间字典”。字典是可变的,因此,我们也可以向该字典中添加或删除或修改值,如:
dog._age = 3 print(dog.__dict__) """ {'_name': 'Alice', '_age': 3} """ dog.__dict__["address"] = "America" print(dog.__dict__) """ {'_name': 'Alice', '_age': 3, 'address': 'America'} """ dog._name = "Bob" print(dog.__dict__) """ {'_name': 'Bob', '_age': 3, 'address': 'America'} """ del dog.__dict__["address"] print(dog.__dict__) """ {'_name': 'Bob', '_age': 3'} """
因此,在默认情况下,python每创建一个实例对象,就会在该实例对象的命名空间中产生一个 __ dict __ 字典用以存储自身的属性和其他数据。**(不管该对象的父类)这样子做虽然可以很直观的看出对象的命名空间和属性,但是当对象的数量非常多时,也会造成内存压力。使用 __ slots __ 类属性就可以避免在实例对象的空间中创建 __ dict __ 属性,从而节约内存。
代码示例
if False: """ 不使用__slots__的Cat类和使用__slots__类属性的Dog类,可以发现: 1. 当使用了__slots__属性后,Dog类中就不再有__dict__字典属性,而出现了__slots__属性; 2. 在Dog类的实例中,也不再具有__dict__属性了,而具有__slots__属性; 3. 在类中建立__slots__变量,是显式地告知类,本类的实例中至多只有_name和_age两个实例变量,请在创建 实例时在实例对象的内存中直接预留空间,不必再创建__dict__对象。 """ class Cat(object): def __init__(self,name): self._name = name cat = Cat("Alice") print("__dict__" in dir(Cat)) print("__slots__" in dir(Cat)) print(hasattr(cat,"__dict__")) print(hasattr(cat,"__slots__")) print("---------------SPLIT LINE-----------------") class Dog(object): __slots__ = "_name","_age" def __init__(self,name): self._name = name dog = Dog("Bob") print("__dict__" in dir(Dog)) print("__slots__" in dir(Dog)) print(hasattr(dog,"__dict__")) print(hasattr(dog,"__slots__")) # 此时,向dog中加入新的实例属性将会报错 dog._address = "China"
True False True False ---------------SPLIT LINE----------------- False True False True --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-64-c3afab1720dd> in <module>() 32 33 # 此时,向dog中加入新的实例属性将会报错 ---> 34 dog._address = "China" AttributeError: 'Dog' object has no attribute '_address'
子类情况
当两个类之间发生继承关系时, __ slots __ 属性在两个类之间的传递关系可以从以下四个方面进行分析:
""" @ 父类无 __ slots __ ,子类无 __ slots __; @这种情况就是最常见的一种情况,无论在子类中还是父类中,实例变量的属性可以根据用户需求自由设置; @ 代码示例:无 """ """ @ 父类无 __ slots __ ,子类有 __ slots __; @ 从逻辑角度上讲,当父类无 __ slots __ 属性时,就意味着父类的实例对象可以自由地创建实例属性, 那么由于子类是父类的更加具体的表现形式,因此根据OOP的原则,子类理论上也是必须具有父类所 创建的一切实例属性的。但此时父类的属性又是不受限制的,因此子类即使被 __ slots __ 所修饰了 也不能受其限制。这是从OOP的原理和逻辑上讲。 @ 从具体技术上讲,由于父类没有 __ slots __ 属性,因此具有 __ dict__属性,那么继承它的子类也一定会具有__dict__属性 而实例是类的实现,因此实例中也一定会有__dict__属性。这是从OOP的技术上讲。 @ 代码示例 """ class LittleCat(Cat): __slots__ = "_age" def __init__(self,name,age): super().__init__(name) self._age = age lc = LittleCat("little cat",3) print(hasattr(LittleCat,"__dict__")) print(hasattr(LittleCat,"__slots__")) print(hasattr(lc,"__dict__")) print(hasattr(lc,"__slots__")) lc.__dict__["_address"] = "China" lc._hobby = "Play" print(lc.__dict__) print("-----------SPLIT LINE-------------") """ @ 父类有 __ slots __ ,子类无 __ slots __; @ 从逻辑角度上讲,当父类有 __slots__属性时,这是对父类的实例属性做了一定的限制,但是对于继承父类的子类而言 子类是需要对父类做扩展和细化的,因此在子类中出现某些父类所不具有的属性是非常常见的。因此当子类中没有__slots__时 也就意味着,子类是可以摆脱父类的属性限制而进行自由的设置的。因此此时即使父类中没有__dict__属性,但是新生成的子类中 仍然会具有__dict__属性。但是要注意的一点是,由于子类也同时继承了父类的__slots__属性,因此在子类的实例的__dict__属性中 是不会出现父类所限制的属性的。 @ 代码示例 """ class LittleDog(Dog): def __init__(self,name,age,address): super().__init__(name) self._age = age self._address = address ld = LittleDog("Cindy",2,"China") print(hasattr(LittleDog,"__dict__")) print(hasattr(LittleDog,"__slots__")) print(hasattr(ld,"__dict__")) print(hasattr(ld,"__slots__")) print(ld.__dict__) print("-----------SPLIT LINE-------------") """ @ 父类有 __ slots __ ,子类有 __ slots __; @ 这种情况也很容易理解,就是父类和子类都严格的限制了其实例对象的属性,那么子类中是不会出现__dict__属性的。 但是要注意的一点是,此时子类的__slots__属性中不需要重复填写父类中__slots__的被限制的属性,而只需要填写新的 属性即可,原因就是__slots__作为一种类属性同样是由继承性的。 @ 代码示例 """ class HugeDog(Dog): __slots__ = "_attack" def __init__(self,name,age,attack): super().__init__(name) self._age = age self._attack = attack # 如果执行本句则将报错 # self._address = 100 hd = HugeDog("Cindy",3,100) print(hasattr(HugeDog,"__dict__")) print(hasattr(HugeDog,"__slots__")) # 注意,没有__dict__ print(hasattr(hd,"__dict__")) print(hasattr(hd,"__slots__")) print(hd.__dict__)
True True True True {'_name': 'little cat', '_address': 'China', '_hobby': 'Play'} -----------SPLIT LINE------------- True True True True {'_address': 'China'} -----------SPLIT LINE------------- True True False True --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-80-a9a73d354c3e> in <module>() 85 print(hasattr(hd,"__dict__")) 86 print(hasattr(hd,"__slots__")) ---> 87 print(hd.__dict__) AttributeError: 'HugeDog' object has no attribute '__dict__'
参考链接: