Python, IoC, исключения и слабая связь

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

#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. В идеале, я бы хотел полностью отделить A от B, чтобы я мог передать любой класс, такой как A, который удовлетворяет тому же интерфейсу, но я не могу из-за исключения (так как он не является частью самого интерфейса)

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

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


1. создать модуль с исключениями и, возможно, базовый класс / метакласс (способ C++/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. Не используйте исключение, просто код возврата. ! C дни вернулись!


Любой другой вариант? Что ты находишь более "питоническим"?


Изменить: Добавлены комментарии, чтобы уточнить цель исключения. Это должно быть обработано в B

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

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 будет использовать A следующим образом:

class B(object):
  def make_him_work(self, afected):
     if afected.can_work():
       afected.work()
Другие вопросы по тегам