古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。

Python高级编程和异步I/O并发编程笔记 7 元类编程

Python admin 313℃ 0评论

property动态属性

__getattr__、__getattribute__魔法函数

__getattr__、__getattribute__两个魔法函数是python解释器内部需要用的方法,其中,前者是在查找不到属性的时候调用,后者是对象查找自身属性必然要先进入的,是所有属性访问的入口,能不重写尽量不要覆盖,当然在写框架的时候,为了控制类的属性访问过程很可能会重写该函数。

属性描述符和属性查找过程

在很多场景下需要对属性的类型(整型、字符串类型等)做出规范和限制,典型的应用场景如djanog的ORM中,虽然如上文提到的property动态属性(@property+@xxx.setter)可以实现某个特定的属性做类型格式限制,但当字段属性很多的情况就不适用了,这时候就需要用到属性描述符,如下例所示做整数类型的验证:

属性查找过程如下:”
如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
首先调用getattribute__。如果类定义了_getattr_方法,那么在_getattribute_抛出 AttributeError 的时候就会调用到_getattr_,而对于描述符(_get_)的调用,则是发生在__getattribute内部的。
user = User(), 那么user.age 顺序如下:

(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__()方法, 否则
(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
(3)如果“age”出现在User或其基类的__dict__中
(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
(3.2)返回__dict__[‘age’]
(4)如果User有__getattr__()方法,调用__getattr__()方法,否则
(5)抛出AttributeError

__new__和__init__的区别

该部分是元类编程的核心知识内容,其中__new__()魔法函数在python新式类才有,即python 2.2之前不存在,在python高级编程如很多框架中用途很大,但日常开发过程中一般使用不到。__new__()和__init__()两个魔法函数的根本区别在于前者接收的第一个参数是类cls,可以用来自定义类的生成过程,后者接收的第一个参数是对象self,二者调用的顺序不一样,先__new__()生成类,然后__init__()生成对象。__new__()是用来控制对象的生成过程,在对象生成之前;__init__()是用来完善对象的;如果__new__()不返回对象,则不会调用__init__()。

自定义元类

上述方法不够灵活,需要创建大量的class语句,所以我们需要使用type。type可以用来动态创建类,提供了三种方法,可以参见type的__init__()方法,如下例展示其中一种方法:

元类是创建类的类,如上所示type就是元类,理解起来就是,对象是有类创建的,类class在python中也是对象,是由type创建的。在日常开发中,我们很少使用type直接创建类,而是使用元类写法。元类能够控制继承它的类的实例化过程的原理在于:在默认情况下,python通过调用type来创建类对象,该类对象在全局中是唯一的。实际情况是,类实例化的过程中,会首先寻找metaclass,通过metaclass创建继承的类,如果在自身找不到metaclass,则会向上查找父类及模块中是否存在metaclass,如果都找不到才会去调用内置的type创建类对象,即metaclass的优先级很高,可以控制类生成的过程。

自定义元类实现ORM

转载请注明:北凉柿子 » Python高级编程和异步I/O并发编程笔记 7 元类编程

喜欢 (1)or分享 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址