【Effective Python】读书笔记-06元类与属性

元类可以说是一种在类之上、超乎于类的概念。简单来说,我们可以认为元类能够拦截 Python 的 class 语句,让系统每次定义类的时候,都能实现某些特殊的行为。

1. 用纯属性与修饰器取代旧式的 setter 与 getter 方法

装饰器@property:

  • @property 最大的缺点是,通过它而编写的属性获取及属性设置方法只能由子类共享。
  • @property 方法必须执行得很快。复杂或缓慢的任务,尤其是涉及 I/O 或者会引发副作用的那些任务,还是用普通的方法来实现比较好。

2. 考虑用 @property 实现新的属性访问逻辑,不要急着重构原有的代码

可以利用@property 给已有的实例属性增加新的功能。

如果你发现自己总是在扩充@property 方法,那可能说明这个类确实应该重构了。在这种情况下,就不要再沿着糟糕的方案继续往下写了。

3. 用描述符来改写需要复用的@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__是怎么通过描述符协议来获取并设置属性的

4. 针对惰性属性使用__getattr____getattribute____setattr__

__getattr__只会在属性缺失时触发,而__getattribute__则在每次访问属性时都要触发。

解决递归问题:使用父类来调用。

5. 用__init_subclass__验证子类写得是否正确

如果用户通过这个元类来定义其他类,那么在那个类真正构造出来之前,我们可以先在__new__里面观察到它的写法并做出修改。

元类可以获知那个类的名称(name)、那个类的所有超类(bases)以及 class 语句体中定义的所有类属性(class_dict)。

6. 用__init_subclass__记录现有的子类

实现自动注册的功能

7. 用__set_name__给类属性加注解

你可能感兴趣的:(python,开发语言)