查看完整摘要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
答案 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)