user178884
user178884

Reputation:

Python: Is it bad form to raise exceptions within __init__?

Is it considered bad form to raise exceptions within __init__? If so, then what is the accepted method of throwing an error when certain class variables are initialized as None or of an incorrect type?

Upvotes: 174

Views: 89783

Answers (8)

Seppo Enarvi
Seppo Enarvi

Reputation: 3663

It's true that the only proper way to indicate an error in a constructor is raising an exception. You'll need to pay some attention on exception safety, though. In object-oriented languages such as C++, the destructor is not called if an exception is thrown in the constructor of an object (meaning that the initialization of the object is incomplete). In Python, the destructor is always called. For example, the following code throws an AttributeError at self.stream.close(), if self.socket.connect() fails:

class Connection:
    def __init__(self, address):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self):
        self.stream.close()
        self.socket.close()

The reason is that the destructor of the incomplete object is called after the connection attempt has failed, before the stream attribute has been initialized.

Edit: As people have pointed out in the comments, it's probably better to use a context manager for managing resources in Python. The alternative below uses a context manager. It doesn't need to raise an exception in the constructor, and if __enter__() raises an exception, __exit__() will not be called.

import socket

class Connection:
    def __init__(self, address):
        self.address = address

    def __enter__(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(self.address)
        self.stream = self.socket.makefile()
        return self.stream

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.stream.close()
        self.socket.close()
        return False

with Connection(("localhost", 22)) as c:
    print(c.readline())

Upvotes: 39

Pétur Ingi Egilsson
Pétur Ingi Egilsson

Reputation: 4493

Do not raise exceptions in constructors invoked by context managers!

Example: If contextlib.closing is used as such:

with closing(Foobar()) as foobar:
    foobar.my_method()

Then foobar.close() will NOT be called if an exception is raised in the objects constructor!

Upvotes: 0

John Millikin
John Millikin

Reputation: 200836

Raising exceptions within __init__() is absolutely fine. There's no other good way to indicate an error condition within an initializer, and there are many hundreds of examples in the standard library where initializing an object can raise an exception.

The error class to raise, of course, is up to you. ValueError is best if the initializer was passed an invalid parameter.

Upvotes: 222

Ctrl-C
Ctrl-C

Reputation: 4172

Raising errors from init is unavoidable in some cases, but doing too much work in init is a bad style. You should consider making a factory or a pseudo-factory - a simple classmethod that returns setted up object.

Upvotes: 4

Salim Fadhley
Salim Fadhley

Reputation: 23018

I concur with all of the above.

There's really no other way to signal that something went wrong in the initialisation of an object other than raising an exception.

In most programs classes where the state of a class is wholly dependant on the inputs to that class we might expect some kind of ValueError or TypeError to be raised.

Classes with side-effects (e.g. one which does networking or graphics) might raise an error in init if (for example) the network device is unavailable or the canvas object cannot be written to. This sounds sensible to me because often you want to know about failure conditions as soon as possible.

Upvotes: 3

Edan Maor
Edan Maor

Reputation: 10052

I don't see any reason that it should be bad form.

On the contrary, one of the things exceptions are known for doing well, as opposed to returning error codes, is that error codes usually can't be returned by constructors. So at least in languages like C++, raising exceptions is the only way to signal errors.

Upvotes: 11

sth
sth

Reputation: 229663

The standard library says:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

Also I don't really see any reason why it should be considered bad form.

Upvotes: 10

Teddy
Teddy

Reputation: 6163

I should think it is the perfect case for the built-in ValueError exception.

Upvotes: 4

Related Questions