30

I want to set up a class that will abort during instance creation based on the value of the the argument passed to the class. I've tried a few things, one of them being raising an error in the __new__ method:

class a():
    def __new__(cls, x):
        if x == True:
            return cls
        else:
            raise ValueError

This is what I was hoping would happen:

>>obj1 = a(True)
>>obj2 = a(False)
ValueError Traceback (most recent call last)

obj1 exists but obj2 doesn't.

Any ideas?

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
moorepants
  • 1,813
  • 2
  • 22
  • 23
  • shouldn't you override `__init__(self, ...)` instead? – matt b Oct 05 '10 at 21:12
  • @matt b. It's not as semantic if then intent is to stop object creation. It does work though. – aaronasterling Oct 05 '10 at 21:28
  • @AaronMcSmooth: Why would raising an exception in `__new__` be preferable to raising one in `__init__`. The result seems the same to me and `__init__` is where all the other initialization code goes. Why shouldn't this go there as well? – Arlaharen Oct 05 '10 at 22:04
  • @Arlaharen. because it's not __initilization__ code. It's __conditional construction__ code. It seems like `__init__` should only throw an exception if it runs into trouble initializing the instance. Here, it's an explicit test that's being done. I'm probably just quibbling though. I would say that if you want to do it in the initializer, just try to use whatever `x` is and let that raise an exception or go through. – aaronasterling Oct 05 '10 at 22:38
  • I really think that the advice one would want to give in general is to treat `__init__` as the constructor. If the construction of the object needs to be aborted, for any reason, it works just as well to raise an exception there. Overriding `__new__` is very rarely needed and for everyday normal classes I think it should be avoided. Doing so keeps the class implementation simple. – Arlaharen Oct 06 '10 at 14:06

2 Answers2

22

When you override __new__, dont forget to call to super!

>>> class Test(object):
...     def __new__(cls, x):
...         if x:
...             return super(Test, cls).__new__(cls)
...         else:
...             raise ValueError
... 
>>> obj1 = Test(True)
>>> obj2 = Test(False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
ValueError
>>> obj1
<__main__.Test object at 0xb7738b2c>
>>> obj2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'obj2' is not defined

Simply returning the class does nothing when it was your job to create an instance. This is what the super class's __new__ method does, so take advantage of it.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
9

Just raise an exception in the initializer:

class a(object):
    def __init__(self, x):
        if not x:
            raise Exception()
aaronasterling
  • 68,820
  • 20
  • 127
  • 125
Arlaharen
  • 3,095
  • 29
  • 26