2015-05-18 6 views
2

Скажем, у нас есть два класса: Class A с особой ошибкой, которая часто возникает и является частью ее функциональности.Python, IoC, Исключения и свободная связь

#a.py 
class AError(Exception): 
    """This exception flags a functional error""" 
    pass 


class A(object): 
    def work(self): 
    """Throws AError when it is tired""" 
    raise AError() #This exception is raised eventually, business code removed for clarity 

Class B, который использует класс A для выполнения некоторых операций.

#b.py 
import a 
class B(object): 
    def make_him_work(self, afected): 
    try: 
     afected.work() 
    except a.AError: 
     pass #This was expected, here will go some business logic 

Это прекрасно работает, но это становится проблемой, когда у меня есть различные типы A. В идеале, я хотел бы, чтобы полностью отделить А от В, так что я могу пройти любой класс, как А, удовлетворяет тот же интерфейс, но я не могу из-за исключения (поскольку он не является частью самого интерфейса)

В C++ у меня будет файл заголовка с определением моего интерфейса плюс исключения, которые будут реализованы конкретными классами. Как это обычно решается в Python? Или сказал другой путь, какой самый пифонический подход?

Я думал, что следующие варианты:


1. создать модуль с исключениями и, возможно, базовый класс/метакласса (С ++/Java путь)

#common.py 
class AErrorBase(Exception): 
    pass 


class AIface(object): 
    def work(self): 
    raise NotImplemented() 

.

#a.py 
import common 
class AError(common.AErrorBase): 
    pass 


class A(common.AIface): 
    def work(self): 
    """Throws AError when it is tired""" 
    raise AError() 

.

#b.py 
import common 
class B(object): 
    def make_him_work(self, afected): 
    try: 
     afected.work() 
    except common.AErrorBase: 
     pass #This was expected 

2. Исключение передачи в качестве аргумента

#a.py 
class AError(Exception): 
    pass 


class A(object): 
    def work(self): 
    """Throws AError when it is tired""" 
    raise AError() 

.

#b.py 
class B(object): 
    def make_him_work(self, afected, ex_type): 
    try: 
     afected.work() 
    except ex_type: 
     pass #This was expected 

3. Исключение в качестве атрибута класса, так что становится частью интерфейса.

#a.py 
class A(object): 
    def work(self): 
    """Throws AError when it is tired""" 
    raise AError() 
    class AError(Exception): 
    pass 

.

#b.py 
class B(object): 
    def make_him_work(self, afected): 
    try: 
     afected.work() 
    except afected.AError: 
     pass #This was expected 

4. Использование исключение Dont, только код возврата. ! C дней назад!


Любой другой вариант? Что вы находите более «pythonic»?


Редактировать: Добавлены комментарии, разъясняющие цель исключения. Он должен быть обработан в B

Примечание: Это может быть совершенно так, что я приближаюсь к проблеме с моим старым фоном C++, я просто хочу знать, как вы применяете IoC в python, когда у нас есть исключения.Вы можете сказать, все мои подходы мусор, и я должен делать это по-другому

+0

Учитывая, что вы просто 'pass' после ошибки, почему бросить его на всех? Обрабатывайте его в 'A' и, например, return 'True' (завершено) или' False' (по какой-то ожидаемой причине) остановлен вызывающим абонентом из 'work'. Тогда вы знаете, что любая ошибка, которая делает ее «B», является неожиданным. Подобных вопросов довольно сложно ответить в абстрактном/общем случае. – jonrsharpe

+0

примечание бизнес-кода удаляется для простоты, '' A'' бросает, потому что он не может обработать ошибку, а просто сообщить об этом, и он находится в '' B'', где у нас будет некоторый бизнес-код для обработки такого исключения. –

+1

Без конкретного примера на этот вопрос нельзя ответить, и с ним я подозреваю, что это все равно будет на стороне мнения. Если вы посмотрите на стандартные библиотечные модули с определенными ошибками, вы можете сделать хуже, чем принять соглашение, которое они используют. – jonrsharpe

ответ

1

Моего класс будет выглядеть следующим образом:

class A(object): 
    def can_work(self): 
    "returns True if an A can work otherwise False (e.g. is an A is tired)" 
    return ... 

    def work(self): 
    assert not self._is_tired, "Test if self.can_work() first!" 
    ... 

Таким образом, вы позволяете пользователям A, чтобы проверить, должны ли они использовать работу , Утверждение полезно для отладки и обеспечения того, чтобы вы или другие люди не забывали об интерфейсе.

класса B будет использовать следующим образом:

class B(object): 
    def make_him_work(self, afected): 
    if afected.can_work(): 
     afected.work() 
+0

Но как я могу обработать исключение в B? Когда я утверждаю, что я просто получаю '' AssertionError'', я хочу иметь «осмысленное» исключение, которое указывает обработчику, что что-то не работает должным образом –

+0

Существует разница между чем-то неожиданным -> Исключение, и что-то не должно было быть done -> нарушение контрактов/интерфейсов/допущений. Я добавлю код для B. – User

+0

Это совершенно справедливое явление. Подумайте в B как сетевой сокет, и вы хотите передать сетевую ошибку до вашего клиента. Но все же вы не хотите, чтобы ваш клиент зависел от конкретной реализации сетевого сокета. –