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

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

概述: 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 __ 属性在两个类之间的传递关系可以从以下四个方面进行分析:

  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 = 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__'

参考链接

上一篇:android – Retrofit 2 RxJava – Gson – “全局”反序列化,更改响应类型


下一篇:android – Realm Retrofit Rxjava