Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

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, 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?

like image 520
Ian Avatar asked Dec 06 '22 01:12

Ian


2 Answers

__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().)

like image 73
Thomas Wouters Avatar answered Dec 07 '22 13:12

Thomas Wouters


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
like image 20
Ian Avatar answered Dec 07 '22 15:12

Ian