代码之家  ›  专栏  ›  技术社区  ›  Ross MacArthur

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

  •  1
  • Ross MacArthur  · 技术社区  · 6 年前

    见全部要点 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'
    

    但是我们在子类化的时候有问题,因为如果你想子类化 __初始化__ 方法发生的事情当然是子类化的方法被覆盖了。

    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 回复  |  直到 6 年前
        1
  •  1
  •   Aran-Fey Kevin    6 年前

    无限递归是由于 type 在此行中:

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

    如果 bases 是的实例 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)