这里是可执行代码,可在Python 2.7中使用,但会在Python 3.6中导致错误:
import six
class AMeta(type):
def __new__(cls, name, bases, attrs):
module = attrs.pop('__module__')
new_attrs = {'__module__': module}
classcell = attrs.pop('__classcell__', None)
if classcell is not None:
new_attrs['__classcell__'] = classcell
new = super(AMeta, cls).__new__(
cls, name, bases, new_attrs)
new.duplicate = False
legacy = super(AMeta, cls).__new__(
cls, 'Legacy' + name, (new,), new_attrs)
legacy.duplicate = True
return new
@six.add_metaclass(AMeta)
class A():
def pr(cls):
print('a')
class B():
def pr(cls):
print('b')
class C(A,B):
def pr(cls):
super(C, cls).pr() # not shown with new_attrs
B.pr(cls)
print('c') # not shown with new_attrs
c = C()
c.pr()
# Expected result
# a
# b
# c
我收到以下错误:
Traceback (most recent call last):
File "test.py", line 28, in <module>
class C(A,B):
TypeError: __class__ set to <class '__main__.LegacyC'> defining 'C' as <class '__main__.C'>
C继承自使用元类AMeta生成的A。它们是测试类,AMeta的目标是使用2个不同的文件夹执行所有测试:默认文件夹和旧文件夹。
我找到了一种删除此错误的方法,方法是从attrs中删除 classcell ,然后返回 new = super(AMeta,cls)。 new < / strong>(cls,名称,碱基,attrs)(不是 new_attrs ),但这似乎不正确,如果正确,我想知道为什么。
new_attrs的目标是由该SO question或documentation引起的,它的基本相反:在修改attrs时,请确保保留 classcell ,因为它在Python 3.6中已弃用,并会在Python 3.8中导致错误。 请注意,在这种情况下,它删除了pr定义,因为它们没有传递给 new_attrs ,因此打印了'b'而不是'abc',但是与这个问题无关。
是否可以调用元类AMeta的 new 中的多个super()。 new ,然后从继承自该类的C类中调用它们一个?
没有嵌套继承,不会出现错误,如下所示:
import six
class AMeta(type):
def __new__(cls, name, bases, attrs):
new = super(AMeta, cls).__new__(
cls, name, bases, attrs)
new.duplicate = False
legacy = super(AMeta, cls).__new__(
cls, 'Duplicate' + name, (new,), attrs)
legacy.duplicate = True
return new
@six.add_metaclass(AMeta)
class A():
def pr(cls):
print('a')
a = A()
a.pr()
# Result
# a
那么也许做些什么来解决这个问题是
先谢谢了
答案 0 :(得分:0)
我能弄清你的问题 是什么,以及如何解决
问题在于,当您做自己的工作时,会将同一个 cell
对象传递给类的两个副本:原始副本和旧版本。
由于它同时存在于两个类中,因此当一个人试图使用它时,它会与另一个使用位置发生冲突-super()
在被调用时会选择错误的祖先类。
cell
对象很挑剔,它们是用本机代码创建的,不能在Python端创建或配置。我可以找到一种创建类副本的方法,方法是使用一个将返回新单元格对象的方法并将其作为__classcell__
传递。
(在尝试使用copy.copy
之前,我还尝试过在copy.deepcopy
对象上运行classcell
/ cellfactory
-无效)
为了重现问题并找出解决方案,我为您的元类创建了一个简单的版本,仅Python3。
from types import FunctionType
legacies = []
def classcellfactory():
class M1(type):
def __new__(mcls, name, bases, attrs, classcellcontainer=None):
if isinstance(classcellcontainer, list):
classcellcontainer.append(attrs.get("__classcell__", None))
container = []
class T1(metaclass=M1, classcellcontainer=container):
def __init__(self):
super().__init__()
return container[0]
def cellfactory():
x = None
def helper():
nonlocal x
return helper.__closure__[0]
class M(type):
def __new__(mcls, name, bases, attrs):
cls1 = super().__new__(mcls, name + "1", bases, attrs)
new_attrs = attrs.copy()
if "__classcell__" in new_attrs:
new_attrs["__classcell__"] = cellclass = cellfactory()
for name, obj in new_attrs.items():
if isinstance(obj, FunctionType) and obj.__closure__:
new_method = FunctionType(obj.__code__, obj.__globals__, obj.__name__, obj.__defaults__, (cellclass, ))
new_attrs[name] = new_method
cls2 = super().__new__(mcls, name + "2", bases, new_attrs)
legacies.append(cls2)
return cls1
class A(metaclass=M):
def meth(self):
print("at A")
class B(A):
pass
class C(B,A):
def meth(self):
super().meth()
C()
因此,不仅我创建了一个嵌套函数以使Python运行时创建一个单独的单元对象,然后在克隆的类中使用该对象,而且使用该类的方法也必须重新使用指向新单元格var的新__closure__
创建的。
如果不重新创建方法,它们将无法在克隆类中工作-因为克隆类方法中的super()
会期望指向克隆类本身的单元格,但它指向原始类。
幸运的是,Python 3中的方法是普通函数-使代码更简单。但是,该代码无法在Python 2中运行-因此,只需将其封装在if
块中就不能在Python2上运行。由于__cellclass__
属性甚至不存在,因此完全没有问题。
将上面的代码粘贴到Python shell中之后,我可以同时运行两个方法和super()
的工作方式:
In [142]: C().meth()
at A
In [143]: legacies[-1]().meth()
at A