我花了整整一天的时间来尝试理解python类模型的复杂性,将装饰器,元类和超类弄乱了。

目前,我正在尝试弄清楚某些令牌功能的作用,即新功能(此处为Metaclasses and when/how functions are called的背景故事)

我制作了一个新的模型模块,可以在此处运行测试:

#! /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()


TL; DR,我尝试了这些工作部件之间的几种不同设置。

如果运行此命令,则输出为:

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,这将由它的默认实现来处理,但是在整个程序中不会进行__call__调用。
为什么调用Triple()首先调用元类?如果这是正常的,是否意味着这是具有元类的任何类的典型特征?这种行为与超类相似吗?
我希望电话可以在此列表中的某个位置。在对象的创建例程(例如[prepare],new,init)期间不会调用它吗?


我知道这是很多信息,因此感谢您阅读本文。任何指导将不胜感激。

最佳答案

元类的__new__

方法__new__是创建新实例的调用。因此,它的第一个参数不是实例,因为尚未创建任何实例,而是类本身。

对于元类,预期__new__返回元类的实例,即一个类。它的签名是这样的:

class Meta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        ...



metacls是元类本身。
name是一个字符串,表示正在使用的类的名称
实例化。
bases是将从其继承的类的元组。
namespace是类的名称空间,这是对象
__prepare__返回的值,现在填充了类属性。
**kwargs是实例化时传递给类的任何关键字参数


要实例化一个类,您需要调用type.__new__,这是默认的元类。通常,您可以通过调用super().__new__来实现。

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


元类的__init__

__init__方法的行为与任何其他类都没有不同。如果__new__返回预期类型的​​实例,它将接收创建的实例(这里是类)作为参数。在您的示例中,__new__不返回Meta类型的对象。它返回本身为Meta类型的type

实例化时永远不会调用以下__init__方法。

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


在实例化时调用以下内容,因为Meta.__new__正确返回类型为Meta的对象。

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')


元类的__call__

同样,__call__的行为与任何其他类相同。当您尝试调用元类的实例时将调用它,而在调用元类以创建实例(类)时将调用__new____init__

当然,调用类应该返回一个实例,所以不要忘记调用super().__call__并返回其结果,否则会缩短实例的创建速度,因为type.__call__会调用__new____init__

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()

关于python-3.x - __new __,__ init__和元类(和父类(super class)),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50635635/

10-12 21:39