Python补遗(六)—— __ slots __ 属性

Python补遗(六)—— __ slots __ 属性

概述: Python中每个对象(类实例,类,函数,列表,字典等)都是拥有一个属于自己的命名空间的,其中存储着属于该对象自己的属性或者方法。一般来说,一个对象的命名空间中的数据可以用该对象的 __ dict __ 属性进行查看,如:

class Dog(object):def __init__(self,name):self._name = namedog = 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 = namecat = 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 = namedog = 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) in ()32 33 # 此时,向dog中加入新的实例属性将会报错
---> 34 dog._address = "China"AttributeError: 'Dog' object has no attribute '_address'

子类情况

当两个类之间发生继承关系时, __ slots __ 属性在两个类之间的传递关系可以从以下四个方面进行分析:

  1. 父类无 __ slots __ ,子类无 __ slots __;
  2. 父类无 __ slots __ ,子类有 __ slots __;
  3. 父类有 __ slots __ ,子类无 __ slots __;
  4. 父类有 __ slots __ ,子类有 __ 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 = agelc = 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 = ageself._address = addressld = 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 = ageself._attack = attack
#         如果执行本句则将报错
#         self._address = 100hd = 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) in ()85 print(hasattr(hd,"__dict__"))86 print(hasattr(hd,"__slots__"))
---> 87 print(hd.__dict__)AttributeError: 'HugeDog' object has no attribute '__dict__'

参考链接

  • python slots 详解(上篇)
  • python slots 详解(下篇)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部