libo娱乐官方手机版
  咨询电话:13569722234

立博体育官方客服

元类

前言

本篇博客学习 python 中一种高级概念,元类。在《说文》中这样描述:元,始也(ps:这也太简短了)。三个字,最后一个字还是语气词。。很形象的说明了元的意思,最开始的意思,在道教中元的意思是:开始的,最初。第一。为首的。主要,根本,整体的意思。那么很显然,元类是最开始的类的意思。我们都知道在 python 中一切皆为对象,而对象都继承与 object 类,那么元类与 object 的关系是什么呢?

可以看出元类也是继承自 object 类的。

那么元类和类的关系是什么呢?

简单来讲就是:类生成对象,元类生成自己本身同时实例化其他所有的类。

元类

类作为对象

之前已经说过 python 中一切皆对象,那么类同样也是对象,而且类是元类的对象。当使用关键词class定义一个类时,在代码执行阶段就会创建一个 空的object,并使用元类的__init__方法来出初始化一个类。这个对象(类)本身可以创建对象,因为它是一个类。

# 在内存中创建一个 Foo 对象class ObjectCreator(object): pass

但是同样的它也是一个对象,一个元类的实例对象。

所以从对象层面将和它自己实例出来的对象没有什么不同,因此:

可以将类赋值给一个变量可以复制它可以为类添加其他的属性可以将其作为参数来传递

例如:

>>> print(ObjectCreator) # you can print a class because it"s an object<class "__main__.ObjectCreator">>>> def echo(o):... print(o)...>>> echo(ObjectCreator) # you can pass a class as a parameter<class "__main__.ObjectCreator">>>> print(hasattr(ObjectCreator, "new_attribute"))False>>> ObjectCreator.new_attribute = "foo" # you can add attributes to a class>>> print(hasattr(ObjectCreator, "new_attribute"))True>>> print(ObjectCreator.new_attribute)foo>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable>>> print(ObjectCreatorMirror.new_attribute)foo>>> print(ObjectCreatorMirror())<__main__.ObjectCreator object at 0x8997b4c>

动态创建类

由于类也是对象,因此可以像任何对象一样动态创建它们。 在python中,type 除了可以查看对象的类型,还有一个很强大的功能,type 可以动态的创建类。type 可以将类的描述作为参数并返回一个类。

使用 class关键词时,python 会自动的创建此对象,就像实例化一个对象时,调用的是类中的__init__方法,创建对象时同样的调用了元类中的__init__方法:

该函数有四个参数,第一个参数为 cls表示这是一个由类调用的初始化函数,python 在检测语法的时候发现 class则会把 class 后面的类名当做参数传给 what,bases 表示继承的父类是谁,是个元祖类型(因为会有多继承),dict 是个字典类型,是类的名称空间,可以通过__dict__查看,使用 type来创建一个类:

what = "Music"bases = (object,)dict = {"music_name": "南山忆"}

这和我们使用 class关键词定义一个类没有什么不同:

之前在学习类的时候,我们知道创建一个对象是调用了类中__init__方法,同理在创建类的时候是调用了元类中也就是 type 中的__init__方法,所以我们可以通过改写 type 中的元类来自定义创建类,比如加些判断或者其他的属性:

可惜想象是美好的,这招根本行不通,那么有没有别的办法呢?当然有啦,哈哈哈哈哈

在学习类的三大特性的时候,从父类继承的属性可以改写(多态的思想),那么改写不了元类是因为元类很特殊,那我继承自元类的类肯定可以改写吧。这个到后面再讲,突然发现什么是元类还没讲清楚。。。

什么是元类

我把元类称之为 类的类。元类是创建类的‘’东西‘’,我们定义类来创建对象,类也是对象,所以定义了一个元类用来创建对象。type是 Python 用来创建所有类的元类(不包括 object 类)。其实这和用 str创建字符串对象,int创建整数对象一致的。type只是创建类对象的类。

一切,all thing都是 Python 中的一个对象。这包括整数、字符串、函数和类。所有这些都是对象,所有这些都是从一个类创建的。因此,元类是创建类对象的东西。

__metaclass__属性

除了使用 type 动态创建类以外,要控制类的创建行为,可以使用 metaclass,这也是自定义元类的方法。

metaclass 的意思就是元类:当我们定义了类以后,就可以根据这个类创建出实例,所以先定义类,然后创建实例。但是如果想创建出类呢?那就必须根据 metaclass 创建出类,所以:先定义元类(不自定义时,默认用 type),然后创建类。(大部分情况使用不到 metaclass,除非想自定义元类)。

默认情况下,类是使用 type 构造的。类主体在一个新的名称空间中执行,类名在本地绑定到类型的结果(名称,基,名称空间)。

可以通过类定义行中传递元类关键字参数来定制类的创建过程,或者从包含此类参数的现有类继承。

实例化对象的完整过程

class Foo(Bar): pass

当解释器执行这行代码时,执行以下操作:

Foo 有__metaclass__属性吗?如果有的话,python 会通过 __metaclass__在内存中创建一个名称为 Foo 的类对象。如果 Python 没有找到__metaclass__,它会继续在 Bar 中寻找__metaclass__属性,并尝试做和前面同样的操作。如果 Python 在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还找不到__metaclass__,python 就会用内置的 type 来创建这个类对象。

那么在自定义类是,可以在__metaclass__中放置什么代码呢?

可以放用来创建一个类的东西,type 或者 type的子类都可以放。

以上面的代码为例,我们实例化一个对象obj=Foo(),会先执行 Foo 类中的__new__方法,没有则使用父类的__new__方法,创建一个空对象并返回,然后执行__init__方法(自己有就用自己的,没有就用父类的,这里分两种情况,如果是创建一个类的对象,那就是使用父类的,因为自己没有;如果是创建类,自己有就是用自己的,否则就是用父类的),为创建的对象进行初始化。

obj()会执行 Foo 类的__call__方法,没有则用父类的。现在已经知道。类同样也是对象,是元类的对象,即实例化一个对象(类)时,调用其父类(元类)的__call__方法。

元类处理过程:定义一个类时,使用声明或者默认的元类对该类进行创建,对元类求 type 运算,得到父元类(该类声明元类的父元类),调用父元类的__call__方法,在父元类的__call__方法中,调用该类声明的元类的__new__来创建一个空对象(该方法需要返回一个类对象实例),然后再调用该元类的__init__方法初始化该类对象,最终返回一个类。

    对象时类创建,创建对象时类的__init__方法自动执行,对象()则会执行类的__call__方法;类是由 type 创建的,class 定义的时候 type 的__init__方法自动执行,类()则会执行type 的__call__方法(类的__new__,类的__init__方法)

元类的__new__方法和__init__影响的是创建类的对象行为(不是创建类),父元类的__call__控制对子元类的__new__,__init__的调用,就是说控制类对象的创建和初始化。父元类的__new__和__init__由更上层的元类控制,一般来说,原始 type 是最初的父元类,其__new__和__init__是最具有普遍意义的,即应该是分配内存、初始化相关信息等。元类的__call__方法影响的是创建类的实例对象的行为,所以这时候自定义__call__就可以控制创建类对象的实例对象的行为了。比如单例模式的创建。

__new__和__init__影响的是创建对象的行为,当这些函数在元类中时,影响创建的是类;同理,当这两个函数在普通类中时,影响的是创建普通的对象实例行为。

__call__影响()调用行为,__call__是在创建类的时候调用,即:定义类时就是创建类,此时会调用元类的__call__,如果元类有继承,子元类定义时执行的是父元类的__call__。如果是普通类实例化,调用的是普通类的__call__。(昨晚上卡在这里了,其实实例化一个普通的对象时,都是调用其父类的__call__方法,除了元类,普通类中不会有__call__方法。)

自定义元类

元类的主要目的是在创建类时自动更改类,比如想要将创建的所有类都变成首字母大写的:

class MymetaClass(type): def __call__(cls, *args, **kwargs): if type(args[0]) != str: raise TypeError("参数必须为字符串类型") obj = object.__new__(cls) obj.__init__(*args, **kwargs) return objclass Foo(metaclass=MymetaClass): def __init__(self, name): self.name = nameres = Foo(123)

这就是自定义元类的好处,可以在__call__来对传入的参数进行一些判断来做一些自定义操作。

通过函数

# the metaclass will automatically get passed the same argument# that you usually pass to `type`def upper_attr(future_class_name, future_class_parents, future_class_attr): """ Return a class object, with the list of its attribute turned into uppercase. """ # pick up any attribute that doesn"t start with "__" and uppercase it uppercase_attr = {} for name, val in future_class_attr.items(): if not name.startswith("__"): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val # let `type` do the class creation return type(future_class_name, future_class_parents, uppercase_attr)__metaclass__ = upper_attr # this will affect all classes in the moduleclass Foo(): # global __metaclass__ won"t work with "object" though # but we can define __metaclass__ here instead to affect only this class # and this will work with "object" children bar = "bip"print(hasattr(Foo, "bar"))# Out: Falseprint(hasattr(Foo, "BAR"))# Out: Truef = Foo()print(f.BAR)# Out: "bip"

通过元类

# remember that `type` is actually a class like `str` and `int`# so you can inherit from itclass UpperAttrMetaclass(type): # __new__ is the method called before __init__ # it"s the method that creates the object and returns it # while __init__ just initializes the object passed as parameter # you rarely use __new__, except when you want to control how the object # is created. # here the created object is the class, and we want to customize it # so we override __new__ # you can do some stuff in __init__ too if you wish # some advanced use involves overriding __call__ as well, but we won"t # see this def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): uppercase_attr = {} for name, val in future_class_attr.items(): if not name.startswith("__"): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val return type(future_class_name, future_class_parents, uppercase_attr)

使用元类的__new__方法

因为 type 的__new__不会被覆盖,所以可以使用:

class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): uppercase_attr = {} for name, val in future_class_attr.items(): if not name.startswith("__"): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val # reuse the type.__new__ method # this is basic OOP, nothing magic in there return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)

使用 super 的__new__方法

class UpperAttrMetaclass(type): def __new__(cls, clsname, bases, dct): uppercase_attr = {} for name, val in dct.items(): if not name.startswith("__"): uppercase_attr[name.upper()] = val else: uppercase_attr[name] = val return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

使用元类的代码复杂性背后的原因不是因为元类,而是因为你通常使用元类来依赖于内省,操纵继承,变量__dict__等等来做扭曲的东西。实际上,元类特别适用于制作黑魔法,因此也很复杂。但他们本身很简单:

拦截类的创建自定义类返回修改后的类

为什么要使用元类而不是函数?

既然__metaclass__可以接收任何调用,那么为什么要使用一个类,因为类显然比函数要复杂。

有几个原因:

目的很明确,你知道使用元类会发生什么;可以使用 OOP。metaclass 可以从元类继承,覆盖父方法,元类甚至可以使用元类;如果你指定了元类,但没有使用元类函数,则类的子类将其元类的实例;可以更好地构建代码;可以和__new__,__init__,__call__搭配使用,来使得创建一个类或者一个类实例变得更有创造性。

本文参考与一下两篇文章

https://stackoverflow.com/questions/100003/what-are-metaclasses-in-pythonhttps://www.cnblogs.com/huchong/p/8260151.html感谢

, 1, 0, 9);