[英]How to dynamically generate an intermediate class with Python 3 metaclasses
Consider the case where we have a simple metaclass that generates the __init__
method for a 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
Now this is all good and well to use现在这一切都很好用
test = Test('hello')
assert test.get_message() == 'hello'
But we have problems when subclassing, because if you want to subclass the __init__
method what of course happens is the subclassed method just gets overwritten.但是我们在子类化时遇到了问题,因为如果你想子类化
__init__
方法,当然会发生子类化的方法被覆盖。
class SubTest(Test):
def __init__(self, first, second):
self.first = first
self.second = second
super().__init__(first + ' ' second)
subtest = SubTest('hello', 'there')
This will obviously give the这显然会给
TypeError: init() takes 2 positional arguments but 3 were given
The only way I can think to solve this is to create an intermediate class in the __new__
method of the metaclass and make this the base for the class we are creating.我能想到的解决这个问题的
__new__
方法是在元类的__new__
方法中创建一个中间类,并使其成为我们正在创建的类的基础。 But I can't get this to work, I tried something like this但我不能让它工作,我试过这样的事情
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)
But this gives me recursion error但这给了我递归错误
RecursionError: maximum recursion depth exceeded while calling a Python object
The infinite recursion is caused by the fact that the type
constructor can return an instance of your metaclass.无限递归是由
type
构造函数可以返回元类的实例这一事实引起的。 In this line here:在此行中:
interm_base = type(cname + 'Intermediate', bases, attrs)
If any of the base classes in bases
is an instance of TestType
, then the subclass will also be an instance of TestType
.如果
bases
中的TestType
类是TestType
的实例,则子类也将是TestType
的实例。 That is why Test
can be created with no problems, but SubTest
causes infinite recursion.这就是为什么可以
SubTest
地创建Test
,但SubTest
会导致无限递归。
The fix is simple: Create the intermediate class without an __init__
attribute.解决方法很简单:创建没有
__init__
属性的中间类。 That way if '__init__' in attrs:
will be False
, and the endless recursion is avoided.这样,
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.