如何使用Python 3元类动态生成中间类

时间:2018-10-09 06:22:39

标签: python python-3.x dynamic metaclass

查看完整摘要here

考虑一下我们有一个简单的元类为类生成__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'

但是在子类化时会遇到问题,因为如果您想对__init__方法进行子类化,那当然会发生,就是子类化方法会被覆盖。

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

我认为解决此问题的唯一方法是在元类的__new__方法中创建一个中间类,并将其作为我们正在创建的类的基础。但是我无法使它起作用,我尝试了类似的方法

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

1 个答案:

答案 0 :(得分:1)

无限递归是由于type构造函数可以返回元类的实例而引起的。 在此行中:

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

如果bases中的任何基类是TestType的实例,则子类也将是TestType的实例。这就是为什么可以毫无问题地创建Test,但是SubTest导致无限递归的原因。

修复很简单:创建没有__init__属性的中间类 。这样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)