__new__, __init__, and metaclasses (and superclasses)
我花了一整天的时间试图理解Python类模型的复杂性,与修饰器、元类和超类混在一起。
目前,我正试图弄清楚某些令牌函数的角色,即新的(这里的背景是元类以及何时/如何调用函数)
我制作了一个新的模拟模块来运行测试,这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | #! /usr/bin/env python3 import sys as system import os as operating_system from functools import partial from time import perf_counter as counter class Meta(type): @classmethod def __prepare__(instance, name, supers, *list, **map): print('{} in meta prepare'.format(name)) return {} def __new__(instance, name, supers, attributes, *list, **map): print('{} in meta new'.format(name)) return instance def __init__(self, name, supers, attributes, *list, **map): print('{} in meta init'.format(self)) def __call__(self, *list, **map): print('{} in meta call'.format(self)) return type.__call__(self) print('after call') class Super(object): def __new__(instance, *list, **map): print('{} in Super new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in Super init'.format(self)) def __call__(self, *list, **map): print('{} in Super call'.format(self)) return object.__call__(self) class Other(object): def __new__(instance, *list, **map): print('{} in Other new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in Other init'.format(self)) def __call__(self, *list, **map): print('{} in Other call'.format(self)) return object.__call__(self) class MetaSuper(object, metaclass = Meta): def __new__(instance, *list, **map): print('{} in MetaSuper new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in MetaSuper init'.format(self)) def __call__(self, *list, **map): print('{} in MetaSuper call'.format(self)) return object.__call__(self) class DoubleSuper(Super, MetaSuper): def __new__(instance, *list, **map): print('{} in DoubleSuper new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in DoubleSuper init'.format(self)) Super.__init__(self, *list, **map) MetaSuper.__init__(self, *list, **map) def __call__(self, *list, **map): print('{} in DoubleSuper call'.format(self)) return object.__call__(self) class SuperThenMeta(Super, metaclass = Meta): def __new__(instance, *list, **map): print('{} in SuperThenMeta new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in SuperThenMeta init'.format(self)) Super.__init__(self, *list, **map) def __call__(self, *list, **map): print('{} in SuperThenMeta call'.format(self)) return object.__call__(self) class Triple(Super, Other, metaclass = Meta): def __new__(instance, *list, **map): print('{} in Triple new'.format(instance)) return instance def __init__(self, *list, **map): print('{} in Triple init'.format(self)) Super.__init__(self, *list, **map) Other.__init__(self, *list, **map) def __call__(self, *list, **map): print('{} in Triple call'.format(self)) return object.__call__(self) class Simple(Super): def __new__(instance, *list, **map): print('{} in Simple new'.format(instance)) return instance.__init__(instance, *list, **map) def __init__(self, *list, **map): print('{} in Simple init'.format(self)) Super.__init__(self, *list, **map) Other.__init__(self, *list, **map) def __call__(self, *list, **map): print('{} in Simple call'.format(self)) return object.__call__(self) def main(): #thing = SuperThenMeta() #other = DoubleSuper() last = Super() simp = Simple() trip = Triple() if __name__ == '__main__': main() |
医生,我在这些工件之间试验了几种不同的设置。
如果我运行这个,这是输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | MetaSuper in meta prepare MetaSuper in meta new SuperThenMeta in meta prepare SuperThenMeta in meta new Triple in meta prepare Triple in meta new <class '__main__.Super'> in Super new <class '__main__.Simple'> in Simple new <class '__main__.Simple'> in Simple init <class '__main__.Simple'> in Super init <class '__main__.Simple'> in Other init Traceback (most recent call last): File"./metaprogramming.py", line 134, in <module> main() File"./metaprogramming.py", line 131, in main trip = Triple() TypeError: __new__() missing 3 required positional arguments: 'name', 'supers', and 'attributes' |
由此,我有几个问题:
我是否应该在新函数的末尾调用instance.init(instance,*list,**map)?我不这么认为,但是在"简单"的例子中添加这个似乎是可行的,而"超级"从未达到它的初始值。我的印象是,通过在我自己的调用方法中调用object.call,这将由它的默认实现来处理,但是在整个程序中不会进行任何调用。
为什么首先调用triple()调用元类?如果这是正常的,这是否意味着这是任何具有元类的类的典型情况?这种行为与超类类似吗?
我以为电话会在这个名单上的某个地方。在对象的创建过程中(例如[Prepare]、New、Init),是否不调用它?
我知道这是很多信息,所以感谢您阅读本文,任何指导都将不胜感激。
元类'
方法
在元类的情况下,
1 2 3 | class Meta(type): def __new__(metacls, name, bases, namespace, **kwargs): ... |
metacls 是元类本身。name 是一个字符串,表示实例化。bases 是类将从中继承的类的元组。namespace 是类的名称空间,这是对象由__prepare__ 返回,现在用类属性填充。**kwargs 是在实例化时传递给类的任何关键字参数。
要实例化一个类,需要调用
1 2 3 4 5 6 7 8 | class Meta(type): def __new__(metacls, name, bases, namespace, **kwargs): print('You can do stuff here') cls = super().__new__(metacls, name, bases, namespace, **kwargs) # You must return the generated class return cls |
元类'
下面的
1 2 3 4 5 6 7 | class Meta(type): def __new__(metacls, name, bases, namespace, **kwargs): return None # or anything such that type(obj) is not Meta def __init__(self, name, bases, namespace, **kwargs): # This will never be called because the return type of `__new__` is wrong pass |
下面是在实例化时调用的,因为
1 2 3 4 5 6 | class Meta(type): def __new__(metacls, name, bases, namespace, **kwargs): return super().__new__(metacls, name, bases, namespace, **kwargs) def __init__(self, name, bases, namespace, **kwargs): print('__init__ was called') |
元类'
同样,
当然,调用一个类应该会返回一个实例,所以不要忘记调用
1 2 3 4 5 6 7 8 9 10 11 | class Meta(type): def __call__(self, *args, **kwargs): print(f'An instance was called with {args}') return super().__call__(self, *args, **kwargs) # This declaration if what calls __new__ and __init__ of the metaclass class Klass(metaclass=Meta): pass # This calls the __call__ method of the metaclass instance = Klass() |