Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle exception in __init__

Is it fine to raise an exception in __init__ in python? I have this piece of code:

class VersionManager(object):     def __init__(self, path):         self._path = path         if not os.path.exists(path): os.mkdir(path)         myfunction(path) 

The second line can potentially result in an exception. In that case the object will not be init'ed properly. Is there a better way to handle situations where code in __init__ might throw an exception?

EDIT Added a call to a function after os.mkdir
Added a check to see if directory exists

like image 351
Abhishek Chanda Avatar asked Nov 18 '13 22:11

Abhishek Chanda


People also ask

What is __ init __ () in Python?

The __init__ method is the Python equivalent of the C++ constructor in an object-oriented approach. The __init__ function is called every time an object is created from a class. The __init__ method lets the class initialize the object's attributes and serves no other purpose. It is only used within classes.

Can you raise an exception in constructor Python?

The associated value is usually passed as arguments to the exception class's constructor. User code can raise built-in exceptions.

What is handle exception in Python?

In Python, exceptions can be handled using a try statement. The critical operation which can raise an exception is placed inside the try clause. The code that handles the exceptions is written in the except clause. We can thus choose what operations to perform once we have caught the exception.

What is __ init __ in Django?

__init__ : "__init__" is a reseved method in python classes. It is known as a constructor in object oriented concepts. This method called when an object is created from the class and it allow the class to initialize the attributes of a class.


2 Answers

It is perfectly fine to raise an exception in __init__. You would then wrap the object initiation/creation call with try/except and react to the exception.

One potential odd result though is that __del__ is run anyway:

class Demo(object):     def __init__(self, value):         self.value=value         if value==2:             raise ValueError     def __del__(self):         print '__del__', self.value   d=Demo(1)     # successfully create an object here d=22          # new int object labeled 'd'; old 'd' goes out of scope               # '__del__ 1' is printed once a new name is put on old 'd'               # since the object is deleted with no references  

Now try with the value 2 that we are testing for:

Demo(2) Traceback (most recent call last):   File "Untitled 3.py", line 11, in <module>     Demo(2)              File "Untitled 3.py", line 5, in __init__     raise ValueError   ValueError  __del__ 2 # But note that `__del__` is still run. 

The creation of the object with value 2 raises a ValueError exception and show that __del__ is still run to clean up the object.

Keep in mind that if you raise an exception during __init__ your object will not get a name. (It will, however, be created and destroyed. Since __del__ is paired with __new__ it still gets called)

ie, just like this does not create x:

>>> x=1/0 Traceback (most recent call last):   File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero >>> x Traceback (most recent call last):   File "<stdin>", line 1, in <module> NameError: name 'x' is not defined 

Potential sneakier:

>>> x='Old X' >>> x=1/0 Traceback (most recent call last):   File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> x 'Old X' 

Same thing if you catch an exception of __init__:

try:     o=Demo(2) except ValueError:     print o          # name error -- 'o' never gets bound to the object...                      # Worst still -- 'o' is its OLD value! 

So don't try to refer to the incomplete object o -- it's gone out of scope by the time you get to except. And the name o is either nothing (i.e., NameError if you try to use it) or its old value.

So wrapping up (thanks to Steve Jessop for the User Defined Exception idea), you can wrap the creation of the object and catch the exception. Just figure out how to react appropriately to the OS error you are looking at.

So:

class ForbiddenTwoException(Exception):      pass  class Demo(object):     def __init__(self, value):         self.value=value         print 'trying to create with val:', value         if value==2:             raise ForbiddenTwoException     def __del__(self):         print '__del__', self.value  try:     o=Demo(2) except ForbiddenTwoException:     print 'Doh! Cant create Demo with a "2"! Forbidden!!!'     # with your example - react to being unusable to create a directory...  

Prints:

trying to create with val: 2 Doh! Cant create Demo with a "2"! Forbidden!!! __del__ 2 
like image 162
dawg Avatar answered Oct 17 '22 08:10

dawg


You can wrap the call, as jramirez suggested:

try:     ver = VersionManager(path) except:     raise 

Or you can use a context manager:

class VersionManager(object):     def __init__(self):         #not-so-harmful code         self.path = path      def __enter__(self):         try:             self.path = path             os.mkdir(path)             self.myfunction(path)         except Exception as e:             print e             print "The directory making has failed, the function hasn't been executed."         return self     def __exit__(self, exc_type, exc_value, traceback):         print(exc_type, exc_value, traceback) 

And to run it:

with VersionManager(my_path) as myVersionManager:      #do things you want with myVersionManager 

This way, you'll catch errors inside the with statement as well.

like image 32
aIKid Avatar answered Oct 17 '22 09:10

aIKid