元类以及何时/如何调用函数

Ant*_*osa 1 python metaprogramming metaclass python-3.x

我正在尝试学习元类如何在python 3中工作.我想知道的是:调用哪些函数,按什么顺序,以及它们的签名和返回.

作为一个例子,我知道__prepare__当带有元类的类用参数实例化metaclass, name_of_subclass, bases并返回表示实例化对象的未来命名空间的字典时,会调用它.

我觉得我理解__prepare__这个过程中的一步.我不这样做,虽然,是__init__,__new____call__.他们的论点是什么?他们回报了什么?他们如何互相称呼,或者一般如何进行?目前,我一直在理解何时__init__被召唤.

这是我一直在讨论的一些代码来回答我的问题:

#!/usr/bin/env python3

class Logged(type):

    @classmethod
    def __prepare__(cls, name, bases):
        print('In meta __prepare__')
        return {}

    def __call__(subclass):
        print('In meta __call__')
        print('Creating {}.'.format(subclass))
        return subclass.__new__(subclass)

    def __new__(subclass, name, superclasses, attributes, **keyword_arguments):
        print('In meta __new__')
        return type.__new__(subclass, name, superclasses, attributes)

    def __init__(subclass, name, superclasses, attributes, **keyword_arguments):
        print('In meta __init__')

class Thing(metaclass = Logged):

    def __new__(this, *arguments, **keyword_arguments):
        print('In sub __new__')
        return super(Thing, this).__new__(this)

    def __init__(self, *arguments, **keyword_arguments):
        print('In sub __init__')

    def hello(self):
        print('hello')

def main():
    thing = Thing()
    thing.hello()

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

从这个和一些谷歌搜索,我知道这__new__是一个静态方法,它返回一个对象的实例(通常__new__是定义的对象,但不总是),并且__init__在制作时调用实例.按照这个逻辑,我很困惑为什么Thing.__init__()不被召唤.有人可以照亮吗?

此代码的输出打印'hello',因此正在创建Thing的实例,这进一步让我对init感到困惑.这是输出:

In meta __prepare__
In meta __new__
In meta __init__
In meta __call__
Creating <class '__main__.Thing'>
In sub __new__
hello
Run Code Online (Sandbox Code Playgroud)

任何帮助理解元类将不胜感激.我已经阅读了不少教程,但我错过了其中的一些细节.

Mar*_*ers 5

首先:__prepare__是可选的,如果您所做的只是返回默认的{}空字典,则不需要提供实现.

元类的工作方式与类完全相同,因为当您调用它们时,它们会生成一个对象.类和元类都是工厂.不同之处在于,元类在调用时会生成一个类对象,一个类在调用时会生成一个实例.

类和元类都定义了一个默认__call__实现,它基本上是这样做的:

  1. 调用self.__new__生成一个新对象.
  2. 如果该新对象是self /具有此元类的类的实例,则还要调用__init__该对象.

您生成了自己的__call__实现,但没有实现第二步,这Thing.__init__就是从未调用过的原因.

您可能会问:但该__call__方法是在元类上定义的.这是正确的,所以正是你用这个类调用时调用的方法Thing().所有特殊方法(开始和结束__)都在类型调用(例如type(instance)是类,并且type(class)是元类),因为Python具有来自元类的类的多级实例层次结构; 一__call__类本身的方法是用来做实例调用.对于metaclass()调用,它是type提供__call__实现的对象本身.没错,元类同时是子类和实例type.

编写元类时,只有__call__在要调整调用类时会发生的事情时才应该实现.保留默认实现.

如果我__call__从您的元类中删除该方法(并忽略该__prepare__方法),则Thing.__init__再次调用:

>>> class Logged(type):
...     def __new__(subclass, name, superclasses, attributes, **keyword_arguments):
...         print('In meta __new__')
...         return type.__new__(subclass, name, superclasses, attributes)
...     def __init__(subclass, name, superclasses, attributes, **keyword_arguments):
...         print('In meta __init__')
...
>>> class Thing(metaclass = Logged):
...     def __new__(this, *arguments, **keyword_arguments):
...         print('In sub __new__')
...         return super(Thing, this).__new__(this)
...     def __init__(self, *arguments, **keyword_arguments):
...         print('In sub __init__')
...     def hello(self):
...         print('hello')
...
In meta __new__
In meta __init__
>>> thing = Thing()
In sub __new__
In sub __init__
Run Code Online (Sandbox Code Playgroud)