geekdoc-python-zh/docs/pythoncentral/how-metaclasses-work-techni...

9.7 KiB
Raw Permalink Blame History

Python 2 和 3 中元类在技术上是如何工作的

原文:https://www.pythoncentral.io/how-metaclasses-work-technically-in-python-2-and-3/

metaclass是定义其他类的类型/类的类/对象。在 Python 中,metaclass可以是class、函数或任何支持调用接口的对象。这是因为要创建一个class对象;它的metaclass是用class名称、基类和属性(方法)调用的。当没有定义metaclass时(通常是这样),使用默认的metaclass type

例如:

[python]

Here metaclass points to the metaclass object.

class ExampleClass(metaclass=type): pass [/python]

[python]

Here metaclass points to the metaclass object.

class ExampleClass(object): metaclass = type pass [/python]

创建class时,解释器:

  1. 获取class的名称。
  2. 获取class的基类。
  3. 获取classmetaclass。如果它被定义,它将首先使用这个。否则,它将检查metaclass的基类。如果在base class中找不到metaclass,则使用type object代替。
  4. 获取class中的变量/属性,并将它们存储为一个字典。
  5. 将该信息作为metaclass(name_of_class, base_classes, attributes_dictionary)传递给metaclass,并返回一个class对象。

例如:


# type(name, base, attrs)

# name is the name of the class

# base is a tuple of base classes (all methods/attributes are inherited

# from these) attrs is a dictionary filled with the class attributes

classObject = type('ExampleClass', (object,) ,{})

当 type 被调用时,它的__call__方法被调用。这个方法依次调用__new____init__方法。__new__方法创建一个新对象,而__init__方法初始化它。我们可以轻松地玩方法。这是一个工作示例:

[python] class a: def init(self, data): self.data = data

def getd3(self): 返回 self.data * 3

class my meta(type): def _ _ New _ _(meta nameclassnamebaseclassesattrs): print(' New called with ') print(' meta name 'metaname) print('classname 'class name) print(' base classes 'baseclasses) print('attrs 'attrs) attrs[' get data ']= a . _ _ dict _ _[' get D3 ']

attrs[' get data ']= a . get D3__new__(元名,类名,基类,属性)

def init(classobjectclassnamebaseclassesattrs): print(' init called with ') print(' class object 'class object) print(' class name 'class name) print(' base classes 'baseclasses) print('attrs 'attrs)

Kls 类(元类=MyMeta): def init(selfdata): self.data = data

def printd(self): 打印(self.data)

我= kls(arun)【t0 I . printtd()【print(我. getdata())

运行代码时,我们得到:


New called with

metaname <class '__main__.MyMeta'>

classname Kls

baseclasses ()

attrs {'__module__': '__main__', 'printd': <function printd at 0x7f3ebca86958>, '__init__': <function __init__ at 0x7f3ebca868d0>}

init called with

classobject <class '__main__.Kls'>

classname Kls

baseclasses ()

attrs {'__module__': '__main__', 'getdata': <function getd3 at 0x7f3ebca86408>, 'printd': <function printd at 0x7f3ebca86958>, '__init__': <function __init__ at 0x7f3ebca868d0>}

arun

arunarunarun

[/shell]

[python] class a(object): def init(self, data): self.data = data

def getd3(self): 返回 self.data * 3

class my meta(type): def _ _ New _ _(meta nameclassnamebaseclassesattrs): print ' New called with ' print ' meta name 'metaname print 'classname 'classname print 'baseclasses 'baseclasses print 'attrs 'attrs attrs[' get data ']= a . _ _ dict _ _[' get D3 ']

attrs[' get data ']= a . get D3

返回类型。new(元名,类名,基类,属性)

def init(classobjectclassnamebaseclassesattrs): print ' init called with ' print ' class object 'classobject print 'classname 'classname print 'baseclasses 'baseclasses print 'attrs 'attrs

Kls 类(object): _ _ 元类 __ = MyMeta

def init(selfdata): self.data = data

def printd(self): 打印自我数据

我= kls(arun)【t0 我. printtd() 我. getdata()

运行代码时,我们得到:


New called with

metaname <class '__main__.MyMeta'>

classname Kls

baseclasses (<type 'object'>,)

attrs {'__module__': '__main__', '__metaclass__': <class '__main__.MyMeta'>, 'printd': <function printd at 0x7fbdab0176e0>, '__init__': <function __init__ at 0x7fbdab017668>}

init called with

classobject <class '__main__.Kls'>

classname Kls

baseclasses (<type 'object'>,)

attrs {'__module__': '__main__', 'getdata': <function getd3 at 0x7fbdab017500>, '__metaclass__': <class '__main__.MyMeta'>, 'printd': <function printd at 0x7fbdab0176e0>, '__init__': <function __init__ at 0x7fbdab017668>}

arun

arunarunarun

[/shell]

通常我们只需要覆盖一个方法__new____init__。我们也可以用function来代替class。这里有一个例子:

[python] def meta_func(name, bases, attrs): print('meta function called with', name, bases, attrs) nattrs = {'mod' + key:attrs[key] for key in attrs} return type(name, bases, nattrs)

MyMeta = meta_func

Kls 类(元类=MyMeta): def setd(selfdata): self.data = data

def getd(self): 返回 self.data

k = Kls() k . modsetd(' arun ') print(k . modgetd())

为我们提供了以下输出:


meta function called with Kls () {'setd': <function setd at 0x7f3bafe7cd10>, '__module__': '__main__', 'getd': <function getd at 0x7f3bafe7cd98>}

arun

[/shell]

[python] def meta_func(name, bases, attrs): print 'meta function called with', name, bases, attrs nattrs = {'mod' + key:attrs[key] for key in attrs} return type(name, bases, nattrs)

MyMeta = meta_func

Kls 类(object): _ _ 元类 __ = MyMeta

def setd(selfdata): self.data = data

def getd(self): 返回 self.data

k = Kls() k . modsetd(' arun ') 打印 k.modgetd()

为我们提供了以下输出:


meta function called with Kls (<type 'object'>,) {'setd': <function setd at 0x88b21ec>, 'getd': <function getd at 0x88b22cc>, '__module__': '__main__', '__metaclass__': <function meta_func at 0xb72341b4>}

arun

[/shell]

除了修改基类和要创建的类的方法,元类还可以修改实例创建过程。这是因为当我们创建一个实例(ik = Kls())时,这就像调用class Kls。需要注意的一点是,无论何时我们调用一个对象,它的类型的__call__方法都会被调用。所以在这种情况下,class类型是metaclass,因此它的__call__方法将被调用。我们可以这样检查:

[python] class MyMeta(type): def call(clsname, *args): print('MyMeta called with') print('clsname:', clsname) print('args:', args) instance = object.new(clsname) instance.init(*args) return instance

Kls 类(元类=MyMeta): def init(selfdata): self.data = data

def printd(self): 打印(self.data)

ik = Kls(' arun ') ik . printd() [/python]

[python] class MyMeta(type): def call(clsname, *args): print 'MyMeta called with' print 'clsname:', clsname print 'args:' ,args instance = object.new(clsname) instance.init(*args) return instance

Kls 类(object): _ _ 元类 __ = MyMeta

def init(selfdata): self.data = data

def printd(self): 打印自我数据

ik = Kls(' arun ') ik . printd() [/python]

输出如下所示:


MyMeta called with

clsname: <class '__main__.Kls'>

args: ('arun',)

arun

有了这些信息,如果我们开始讨论class的创建过程,它以调用metaclass对象结束,该对象提供了一个class对象。事情是这样的:


Kls = MetaClass(name, bases, attrs)

因此这个调用应该调用metaclass的类型。metaclass型是metaclassmetaclass!我们可以这样检查:

[python] class SuperMeta(type): def call(metaname, clsname, baseclasses, attrs): print('SuperMeta Called') clsob = type.new(metaname, clsname, baseclasses, attrs) type.init(clsob, clsname, baseclasses, attrs) return clsob

class MyMeta(typemetaclass = super meta): def _ _ call _ _(clsargs * kwargs): print(' MyMeta called 'clsargskwargs) ob = object。new(cls*args) ob。init(*args) 返回 ob

打印(“创建类”)

Kls 类(元类=MyMeta): def init(selfdata): self.data = data

def printd(self): 打印(self.data)

print(' class created ') ik = Kls(' arun ') ik . printd() ik2 = Kls(' avni ') ik2 . printd() [/python]

[python] class SuperMeta(type): def call(metaname, clsname, baseclasses, attrs): print 'SuperMeta Called' clsob = type.new(metaname, clsname, baseclasses, attrs) type.init(clsob, clsname, baseclasses, attrs) return clsob

class MyMeta(type): _ _ metaclass _ _ = super meta def _ _ call _ _(clsargs * kwargs): print ' MyMeta called 'clsargskwargs ob = object。new(cls*args) ob。init(*args) 返回 ob

打印“创建类”

Kls 类(object): _ _ 元类 __ = MyMeta

def init(selfdata): self.data = data

def printd(self): 打印自我数据

打印“类别已创建”

我= kls(arun)【t0 I . printd() ik2 = kls(名称) ik2 . print TD() [/python]

为我们提供了以下输出:


create class

SuperMeta Called

class created

MyMeta called class '__main__.Kls' ('arun',) {}

arun

MyMeta called &lt;class '__main__.Kls' ('avni',) {}

avni