温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Python metaclass的原理及应用

发布时间:2021-08-31 10:59:31 阅读:138 作者:chen 栏目:编程语言
Python开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

这篇文章主要介绍“Python metaclass的原理及应用”,在日常操作中,相信很多人在Python metaclass的原理及应用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python metaclass的原理及应用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

元编程(meta programming)是一项很神奇的能力,可以通过代码在运行时动态生成代码。

元类(meta classes)是 Python 提供的一种元编程的能力。在 Python 中,类也是一种对象,那么类这种对象就是元类的实例,所以我们可以在运行时通过实例化元类动态生成类。

使用 type “函数”

首先我们来了解一下 type,type 可以作为函数使用,用来获得对象的类型:

>>> class Foo:...     pass>>> obj = Foo()>>> obj.__class__<class '__main__.Foo'>>>> 
type(obj)<class '__main__.Foo'>>>> 
obj.__class__ is type(obj)True

实际上 type 并不是一个函数,而是一个类,我们可以使用 type(type) 来确定一下:

>>> type(type)<class 'type'>

type 实际上不只是类,而是一个“元类”。我们接下来要可以看到,所有的元类都需要继承自 type。type 是所以类的元类,所以在上面的例子中 x 是 Foo 的实例,Foo 是 type 的实例,type 又是他自己的实例。

用 type 动态创建类

如果传递给 type 的参数是三个的时候,type 的语义就不再是返回给定参数的类,而是实例化生成一个新的类。

type(name: str, bases: tuple, namespace: dict)

第一个参数是新生成的类的名字;第二个参数是新生成的类的基类列表;第三个参数是要个这个类绑定的属性的列表,比如说这个类的一些方法。实际上 class Foo 这种语法只是使用 type 生成类的语法糖而已。

最简单的一个例子,比如我们要创建 Foo[0..9] 这些类,可以这样做:

classes = []for i 
in range(10):    cls = type("Foo%s" % i, tuple(), {})    classes.append(cls)# 就像使用普通类一样初始化 Foo0foo0  = clssses[0]()

如果要实现类的方法,一定要记得同样是要使用 self 变量的。在 Python 中 self 只是一个约定俗成的变量,而不是关键字。

def __init__(self, name):    self.name = namedef print_name(self):    print(self.name)Duck = type("Duck", tuple(), {"__init__": __init__, 
"print_name": print_name})duck = Duck("Donald")duck.print_name()# Donald

创建自己的元类

首先我们来回顾一下 Python 中类的初始化过程:

foo = Foo()

当这条语句运行的时候,Python 会依次调用 Foo 的 __new__ 和 __init__ 方法。其中 __new__ 方法在 __init__ 之前调用,并返回已经创建好的新对象,而 __init__ 函数是没有返回结果的。一般情况下,我们都会覆盖 __init__ 方法来对新创建的对象做一些初始化操作。

现在回归到元类上,进入烧脑部分。前面我们说过元类的实例化就是类,所以大致相当于:

Foo = MetaFoo(name, bases, attrs)  # MetaFoo 默认情况下是 typefoo = Foo()

默认情况下,所有类的元类是 type,也就是在这个类是通过 type 来创建的,这和前面说的通过 type 来动态创建类也是一致的。

那么怎样定义一个 MetaFoo 呢?只需要继承自 type 就行了。因为元类的实例化就是类的创建过程,所以在元类中,我们可以修改 __new__ 来在 __init__ 之前对新创建的类做一些操作。

>>> class MetaFoo(type):...     def __new__(cls, name, bases, namespace):...         x = super().__new__(cls, name, bases, namespace)  # super实际上就是 type...         x.bar = 
100  # 为这个类增加一个属性...         return x...>>> Foo = MetaFoo("Foo", tuple(), {})  # MetaFoo 在这里就相当于 type 了,可以动态创建类>>> Foo.bar100>>> foo = Foo()>>> foo.bar100

在这里我们创建了 MetaFoo 这个元类,他会给新创建的类增加一个叫做 bar 的属性。

在实际的代码中,我们一般还是不会直接动态生成类的,还是调用 class Foo 语法来生成类比较常见一点,这时候可以指定 metaclass 参数就好了。可以通过 Foo(metaclass=MetaFoo) 这种方式来指定元类。

class Foo(metaclass=MetaFoo):    pass

这种定义和上面的元类用法效果完全是一致的。

一个现实世界的元类例子

在 django.models 或者 peewee 等 ORM 中,我们一般使用类的成员变量来定义字段,这里就用到了元类。

class Field:    passclass IntegerField(Field):    passclass CharField(Field):    passclass MetaModel(type):    def __new__(meta, name, bases, attrs):        # 这里最神奇的是:用户定义的类中的 bases 和 attrs 都会作为参数传递进来        fields = {}        for key, value 
in attrs.items():            if isinstance(value, Field):                value.name = 
'%s.%s' % (name, key)                fields[key] = value        for base 
in bases:            if hasattr(base, 
'_fields'):                fields.update(base._fields)        attrs['_fields'] = fields        return type.__new__(meta, name, bases, attrs)class Model(metaclass=MetaModel):    pass

这样用户使用的时候就可以这样定义:

>>> class A(Model):...     foo = IntegerField()...>>> class B(A):...     bar = CharField()...>>> B._fields{'foo': Integer('A.foo'), 
'bar': String('B.bar')}

程序在执行的时候就可以直接访问 X._fields,而不用每次都通过反射遍历一次,从而提高效率以及做一些验证。

不过,其实这个完全可以通过装饰器来实现:

def model(cls):    fields = {}    for key, value 
in vars(cls).items():        if isinstance(value, Field):            value.name = 
'%s.%s' % (cls.__name__, key)            fields[key] = value    for base 
in cls.__bases__:        if hasattr(base, 
'_fields'):            fields.update(base._fields)    cls._fields = fields    return cls@modelclass A():    foo = IntegerField()class B(A):    bar = CharField()

但是用装饰器的话,就失去了一些类型继承的语义信息。

到此,关于“Python metaclass的原理及应用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:http://blog.itpub.net/69957771/viewspace-2670230/

AI

开发者交流群×