元类可以说是一种在类之上、超乎于类的概念。简单来说,我们可以认为元类能够拦截 Python 的 class 语句,让系统每次定义类的时候,都能实现某些特殊的行为。
装饰器@property
:
可以利用@property 给已有的实例属性增加新的功能。
如果你发现自己总是在扩充@property 方法,那可能说明这个类确实应该重构了。在这种情况下,就不要再沿着糟糕的方案继续往下写了。
描述符协议(descriptor protocol)规定了程序应该如何处理属性访问操作。充当描述符的那个类能够实现__get__
与__set__
方法,这样其他类就可以共用这个描述符所实现的逻辑而无须把这套逻辑分别重写一遍
转义操作:
class Grade:
def __set__(self, instance, value):
print(value)
self.__dict__[instance] = value
def __get__(self, instance, owner):
return self.__dict__[instance]
class Exam:
math = Grade()
english = Grade()
def __repr__(self):
return f'math:{self.math} english:{self.english}'
e = Exam()
e.math = 99
e.english = 88
其中:
e.math = 99
会被转义为:
Exam.__dict__['math'].set(e,99)
存在内存泄漏的问题:Exam 实例全部会被 Grade 中的__dict
所引用,所以指向那些实例的引用数量永远不为 0,无法实现垃圾回收。
解决方法:
WeakKeyDictionary
保存实例,如果运行时系统发现,指向 Exam 实例的引用只剩一个,而这个引用又是由 WeakKeyDictionary 的键所发起的,那么系统会将该引用从这个特殊的字典里删掉,于是指向那个 Exam 实例的引用数量就会降为 0
__getattribute__
是怎么通过描述符协议来获取并设置属性的
__getattr__
、__getattribute__
及__setattr__
__getattr__
只会在属性缺失时触发,而__getattribute__
则在每次访问属性时都要触发。
解决递归问题:使用父类来调用。
__init_subclass__
验证子类写得是否正确如果用户通过这个元类来定义其他类,那么在那个类真正构造出来之前,我们可以先在__new__
里面观察到它的写法并做出修改。
元类可以获知那个类的名称(name)、那个类的所有超类(bases)以及 class 语句体中定义的所有类属性(class_dict)。
__init_subclass__
记录现有的子类实现自动注册的功能
__set_name__
给类属性加注解