gpt4 book ai didi

python - 元类和何时/如何调用函数

转载 作者:太空宇宙 更新时间:2023-11-04 07:29:45 24 4
gpt4 key购买 nike

我正在尝试了解元类在 python 3 中的工作方式。我想知道的是:调用了哪些函数、以什么顺序调用以及它们的签名和返回。

举个例子,我知道当一个带有元类的类被参数 metaclass, name_of_subclass, bases 实例化时,__prepare__ 会被调用,并返回一个代表 future 命名空间的字典实例化的对象。

我觉得我很了解 __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()

从这个和一些谷歌搜索中,我知道 __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

如果能帮助理解元类,我们将不胜感激。我已经阅读了很多教程,但我错过了其中的一些细节。

最佳答案

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

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

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

  1. 调用self.__new__ 生成一个新对象。
  2. 如果那个新对象是自己的一个实例/一个有这个的类元类,然后对该对象调用__init__

您生成了自己的 __call__ 实现,它没有实现第二步,这就是为什么永远不会调用 Thing.__init__ 的原因。

您可能会问:但是 __call__ 方法是在元类上定义的。这是正确的,所以当您使用 Thing() 调用类时,正是调用该方法。所有特殊方法(以 __ 开头和结尾)都是 called on the type (例如 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__

关于python - 元类和何时/如何调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50628756/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com