如何使用 Python 3 metaclasses 动态生成中间 class

How to dynamically generate an intermediate class with Python 3 metaclasses

查看完整要点here

考虑我们有一个简单的 metaclass 的情况,它为 class

生成 __init__ 方法
class TestType(type):

    def __new__(cls, cname, bases, attrs):
        # Dynamically create the __init__ function
        def init(self, message):
            self.message = message

        # Assign the created function as the __init__ method.
        attrs['__init__'] = init

        # Create the class.
        return super().__new__(cls, cname, bases, attrs)


class Test(metaclass=TestType):

    def get_message(self):
        return self.message

现在这一切都很好用了

test = Test('hello')
assert test.get_message() == 'hello'

但是我们在 subclassing 时遇到问题,因为如果你想 subclass __init__ 方法当然会发生 subclassed 方法只是被覆盖。

class SubTest(Test):

    def __init__(self, first, second):
        self.first = first
        self.second = second
        super().__init__(first + ' ' second)

subtest = SubTest('hello', 'there')

这显然会给

TypeError: init() takes 2 positional arguments but 3 were given

我认为解决这个问题的唯一方法是在 metaclass 的 __new__ 方法中创建一个中间体 class 并将其作为 class 的基础=] 我们正在创造。但是我无法让它工作,我试过这样的东西

class TestType(type):

    def __new__(cls, cname, bases, attrs):
        # Dynamically create the __init__ function
        def init(self, message):
            self.message = message

        # If the __init__ method is being subclassed
        if '__init__' in attrs:
            # Store the subclass __init__
            sub_init = attrs.pop('__init__')

            # Assign the created function as the __init__ method.
            attrs['__init__'] = init

            # Create an intermediate class to become the base.
            interm_base = type(cname + 'Intermediate', bases, attrs)

            # Add the intermediate class as our base.
            bases = (interm_base,)

            # Assign the subclass __init__ as the __init__ method. 
            attrs['__init__'] = sub_init

        else:
            # Assign the created function as the __init__ method.
            attrs['__init__'] = init

        # Create the class.
        return super().__new__(cls, cname, bases, attrs)

但这给了我递归错误

RecursionError: maximum recursion depth exceeded while calling a Python object

无限递归是由于 type 构造函数可以 return 你的元 class 的一个实例。 在此处的这一行中:

interm_base = type(cname + 'Intermediate', bases, attrs)

如果 bases 中的任何基础 class 是 TestType 的实例,那么子 class 也将是 [=14= 的实例].这就是为什么 Test 可以毫无问题地创建,但是 SubTest 导致无限递归。

修复很简单:创建没有 __init__ 属性的中间 class 。这样 if '__init__' in attrs: 将是 False,并且避免了无休止的递归。

class TestType(type):
    def __new__(cls, cname, bases, attrs):
        # Dynamically create the __init__ function
        def init(self, message):
            self.message = message

        # If the __init__ method is being subclassed
        if '__init__' in attrs:
            # Create an intermediate class to become the base.
            interm_base = type(cname + 'Intermediate', bases, {})

            # Add the intermediate class as our base.
            bases = (interm_base,)
        else:
            # Assign the created function as the __init__ method.
            attrs['__init__'] = init

        # Create the class.
        return super().__new__(cls, cname, bases, attrs)