Исключить обработку в __init__

Можно ли создать исключение в __init__ в python? У меня есть эта часть кода:

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

Вторая строка потенциально может привести к исключению. В этом случае объект не будет инициализирован должным образом. Есть ли лучший способ справиться с ситуациями, когда код в __init__ может генерировать исключение?

ИЗМЕНИТЬ Добавлен вызов функции после os.mkdir
Добавлена ​​проверка, чтобы увидеть, существует ли каталог

Ответ 1

Совершенно прекрасно создавать исключение в __init__. Затем вы завершаете вызов инициализации/создания объекта с помощью try/except и реагируете на исключение.

Один потенциальный нечетный результат состоит в том, что __del__ выполняется в любом случае:

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 

Теперь попробуйте со значением 2, которое мы тестируем для:

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.

Создание объекта со значением 2 вызывает исключение ValueError и показывает, что __del__ все еще выполняется, чтобы очистить объект.

Имейте в виду, что если вы вызываете исключение во время __init__, ваш объект не получит имя. (Однако он будет создан и уничтожен. Поскольку __del__ сопряжен с __new__, он все равно вызывается)

т.е. так же, как это не создает 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

Потенциальный подлый:

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

То же самое, если вы поймаете исключение __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!

Поэтому не пытайтесь ссылаться на неполный объект o - он выходил из области действия к моменту, когда вы дойдете до except. И имя o ничто (т.е. NameError, если вы пытаетесь его использовать) или его старое значение.

Итак, обертывание (спасибо Стив Джессопу за идею Пользовательское исключение), вы можете обернуть создание объекта и уловить исключение. Просто выясните, как правильно реагировать на ошибку ОС, на которую вы смотрите.

Итак:

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... 

Печать

trying to create with val: 2
Doh! Cant create Demo with a "2"! Forbidden!!!
__del__ 2

Ответ 2

Вы можете обернуть вызов, как предположил jramirez:

try:
    ver = VersionManager(path)
except:
    raise

Или вы можете использовать диспетчер контекстов:

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)

И для запуска:

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

Таким образом, вы также будете ломать ошибки внутри оператора with.

Ответ 3

Мое любимое - просто выводить ошибки на консоль и идти дальше:

import sys, os, traceback

class Myclass
    def __init__(self, path):
        self._path = path

        """Risky Code"""
        try:
            os.mkdir(path) 
        except:
            traceback.print_exc(file = sys.stdout)

Таким образом, исключение будет печатать больше как предупреждение, а не реальное исключение.

Ответ 4

Вы можете использовать try/except при инициализации объекта.

try:

    ver = VersionManager(my_path)

except Exception as e:
    # raise e or handle error
    print e