关于__new__丢失参数的最佳解决方案有什么建议?

7 投票
3 回答
1891 浏览
提问于 2025-04-16 06:07

今天我才意识到,从Python 2.6开始,__new__这个方法不再支持接收参数了(在文档里没有提到这一点,而根据我所看到的,__new__调用__init__的行为也不太对)。这意味着我的一些功能代码开始出现警告,我想把这些警告去掉。但是我找不到一个优雅的解决办法。

我有一些类,在创建的时候会进行一些优化。所以我有:

class Conjunction(Base):
    def __new__(cls, a, b):
       if a == True: 
          return b
       elif b == True
          return a
       else:
          return super(Conjunction,cls).__new__(cls, a, b)

等等(实际的版本会处理更多的情况)。所以和Guido在这个回复中说的不同(这是我能找到的唯一参考),我的__new__方法确实使用了它的参数,并且不能用重写的__init__函数来替代。

我能做的最好的办法就是把它分成两部分:

def Conjunction(a, b):
   if a == True: 
      return b
   elif b == True
      return a
   else:
      return ConjunctionImpl(a, b)

class ConjunctionImpl(Base):
   # ...

但是这样看起来很丑,而且感觉很糟糕。我是不是漏掉了什么优雅的方法,可以让类的构造函数根据给定的参数返回一些任意的对象?

3 个回答

0

这让我很好奇,因为我在文档中没有看到这个功能被弃用,所以我自己试了一下。

class Foo(object):
    def __new__(cls, a, b):
        if a:
            return a
        elif b:
            return b
        else:
            return super(Foo, cls).__new__(cls, a, b)

    def __init__(self, a, b):
        self.a = a
        self.b = b

class Bar(Foo):
    def __new__(cls, x, y):
        if x:
            return x
        if y:
            return y
        else:
            return super(Bar, cls).__new__(cls, x, y)


foo = Bar(False, False)

在这个例子中,你可以看到我重写了Foo类中的init方法,因为传给new的任何参数都会被转发到__new__试图创建的类实例上。这个foo的实例将是Bar类的,但它会有成员a和b。我没有重写父类的__init__方法,所以它会被调用。方法__new__总是会把它的参数传给__init__。如果你不重写对象的__init__,那么会出错,因为那个方法不接受任何参数。

这就是我对Python 2.7中new用法的理解。根据文档,2.6的用法是类似的。

5

托马斯在他的回答中给了我正确的方向,但我想补充一下,在我的情况下,解决方案其实很简单:在我的基类中添加一个 __new__ 方法,内容如下:

class Base(object):
    def __new__(cls, *args, **kws):
        instance = super(Base, cls).__new__(cls)
        instance.__init__(*args, **kws)
        return instance
13

__new__ 方法并不是“不再接收参数”。在 Python 2.6 中发生的变化是,object.__new__,也就是 object 类的 __new__ 方法,不再忽略传入的参数了。(object.__init__ 也不再忽略参数,不过在 2.6 中这只是个警告。)如果你想在调用 __new____init__ 时传递参数,就不能把 object 作为你继承的终止类。

如果你希望你的代码在 2.6 中正常工作,就需要把 object 替换为一个合适的基类,这个基类能够正确接收额外的参数,并且在调用其他方法时(使用 super())不会把这些参数传递下去。

撰写回答