【问题标题】:How to set the class of value returned by __new__ without using super?如何在不使用super的情况下设置__new__返回的值的类?
【发布时间】:2014-10-09 15:52:05
【问题描述】:

我希望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类型的对象)返回的值作为变量@987654331的起始值@ 在构造函数中。如果我只是这样编码:

class B(A):
    def __new__(cls, ...):
        ret = A_Factory(...)

        # modify ret as needed

        return ret  # type(ret) is A

...那么type(B(...)) 将是A,而不是B

在上面B.__new__ 的第二个版本中设置ret 类的正确方法是什么,type(B(...))B,更一般地说,对于任何直接或间接子类@987654340 B,type(C(...))的@是C?


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

【问题讨论】:

  • 您坚持使用__new__ 还是愿意接受替代解决方案?

标签: python factory


【解决方案1】:

您可以将实例的__class__ 属性设置为传递给__new__cls

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))

输出:

2
<class '__main__.B'>
True
True
<class '__main__.C'>

不过,这有一些限制。 commit that added the ability to assign to __class__ 列出了它们:

当对象结构是 相同的。我希望结构等效性测试足够严格。 它只允许在旧类型和新类型的情况下进行分配:

  • 基本尺寸相同
  • 物品尺寸相同
  • 具有相同的字典偏移量
  • 具有相同的弱列表偏移量
  • 具有相同的 GC 标志位
  • 有一个相同的公共基础,除了字典和弱列表(可能在相同的偏移量处单独添加
    两种类型)

如果您在任一类上定义了__slots__,它们还需要具有相同的__slots__。在实践中,这通常意味着这两个类都需要在 Python 中实现。尝试使用常规 Python 类和 inttuple 或其他用 C 实现的东西不太可能奏效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 2012-11-14
    相关资源
    最近更新 更多