我希望B
成为某个类A
的子类,我想覆盖A.__new__
1 。通常,我的代码将具有以下基本结构:
class B(A):
def __new__(cls, ...):
ret = super(B, cls).__new__(cls, ...)
# modify ret as needed
return ret # type(ret) is cls
# ...
通过这种方式,type(B(...))
确实是B
。 (注意:super(B, cls).__new__(cls, ...)
中的省略号不需要表示与B.__new__
签名中省略号表示的项目相同的项目。)
但现在假设我想使用某些工厂方法/函数 A_Factory
(返回类型为A
的对象)返回的值作为起始值构造函数中的变量ret
。如果我只是编码:
class B(A):
def __new__(cls, ...):
ret = A_Factory(...)
# modify ret as needed
return ret # type(ret) is A
...然后type(B(...))
将是A
,而不是B
。
在上面ret
的第二个版本中设置B.__new__
类的正确方法是什么,type(B(...))
是B
,更常见的是,对于C
的任何直接或间接子类B
,type(C(...))
是C
?
1 我知道通常不需要覆盖父类的__new__
,但在这里我对需要的不常见情况感兴趣。
答案 0 :(得分:1)
您可以将实例的__class__
属性设置为传递给cls
的{{1}}:
__new__
输出:
class A(object):
def __init__(self):
self.x = 2
def A_Factory():
return A()
class B(A):
def __new__(cls):
ret = A_Factory()
ret.__class__ = cls # Override the class.
return ret # type(ret) is cls
class C(B):
pass
if __name__ == "__main__":
b = B()
print(b.x)
print(type(b))
print(isinstance(b, B))
print(isinstance(b, A))
c = C()
print(type(C))
但是,这有一些限制。 commit that added the ability to assign to __class__
列出了它们:
当对象结构为时,可以进行
2 <class '__main__.B'> True True <class '__main__.C'>
赋值 相同。我希望结构等价的测试足够严格。 它只允许分配旧的和新的类型:
- 具有相同的基本尺寸
- 具有相同的项目大小
- 具有相同的dict offset
- 具有相同的弱列表偏移量
- 具有相同的GC标志位
- 有一个共同的基础是相同的,除了可能是dict和weaklist(可能在相同的偏移处单独添加) 两种类型)
如果您在任何一个类上定义了它们,它们也需要具有相同的__class__
。实际上,这通常意味着两个类都需要在Python中实现。尝试使用常规Python类和__slots__
或int
或在C中实现的其他内容不太可行。