开发者

Any ideas about the best work around for __new__ losing its arguments?

So, I only realised today that __new__ is deprecated for receiving arguments, as of python 2.6 (it isn't mentioned in the documentation, which is also not true in terms of the behavior of __new__ calling __init__ as far as I can see). This means my functional code has started raising warnings, and I want to rid myself of them. But I can't see an elegant way to work around.

I have a bunch of classes that perform optimizations when they are constructed. So I have

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)

And so on (real versions cover lots more cases). So unlike what Guido says in this response (the only reference to it I can find), my __new__ method does use its arguments, and cannot be replaced by an overridden __init__ function.

The best I can do is to split this in two:

def Conjunction(a,开发者_JAVA百科 b):
   if a == True: 
      return b
   elif b == True
      return a
   else:
      return ConjunctionImpl(a, b)

class ConjunctionImpl(Base):
   # ...

But that is plain ugly and stinks to high heaven. Am I missing an elegant way to have a class constructor return some arbitrary object based on the constructor parameters it is given?


__new__ is not "deprecated for receiving arguments". What changed in Python 2.6 is that object.__new__, the __new__ method of the object class, no longer ignores any arguments it's passed. (object.__init__ also doesn't ignore the arguments anymore, but that's just a warning in 2.6.) You can't use object as the terminating class for your inheritance if you want to pass arguments to __new__ or __init__.

In order for any code to rely on that behaviour to work in 2.6, you just have to replace object as the baseclass, using a baseclass that properly accepts the extra arguments and does not pass them along in the calls it makes (using super().)


Thomas put me right in his answer, but I should add that the solution in my case was trivial: add a __new__ method to my base class with the lines:

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


Well this made me curious because I did not see the deprecation in the documentation so I gave it a try myself.

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)

As you can see in this example I overrode the init in Foo because any args passed to new will be forwarded to the cls instance that __new__ attempts to create. The instance of foo with be of a Bar class but it will have members a and b. I caused the super class's __init__ to be called by not overriding it. The method __new__ always passes its args on to __init__. If you don't override the __init__ for object it will fail since that method takes no args.

That's my take on the usage of new in Python 2.7. According to the docs 2.6 is similar.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜