如何设置__new__返回的值类而不使用super?

时间:2014-10-09 15:52:06

标签: python factory

我希望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的任何直接或间接子类Btype(C(...))C


1 我知道通常不需要覆盖父类的__new__,但在这里我对需要的不常见情况感兴趣。

1 个答案:

答案 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中实现的其他内容不太可行。