Я пишу некоторый шаблонный код, который имеет некоторые абстрактные классы, от которых наследуются другие. Не каждый метод должен запускаться, поэтому абстрактный класс обрабатывает исключения, используя @handleError в качестве декоратора.
Я буду использовать собак, чтобы проиллюстрировать (реальная проблема гораздо более запутанная)
- Вот декоратор обработки исключений (если не удается запустить, вернуть None)
def handleError(f):
def handleProblems(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception:
return None
return handleProblems
- Класс животных, который будет унаследован от
class Animal:
def __init__(self, eat_time, animal_sound):
self.sound = animal_sound
self.eat_time = eat_time
@handleError # <-- if the user messes up "eat", keep going!
def eat(self):
print('Eats for %0.0f seconds' % self.eat_time)
def speak(self):
print(self.sound)
def eat_and_speak(self):
self.eat()
self.speak()
- Так что теперь и "хорошие собаки" и "испорченные собаки" бегут до конца. («плохая собака» просто скажет «гав», конечно)
# good dog
dog = Animal(4, 'woof')
dog.eat_and_speak()
# messed up dog
dog = Animal('four', 'woof')
dog.eat_and_speak()
> 'Eats for 4 seconds'
> 'woof'
> 'woof' . # <-- yay! the messed up dog barked!
Это именно то, что мы хотим, но мы также хотим позволить пользователям наследовать. Например, делая это:
class Dog(Animal):
def eat(self):
print('Dog eats for %0.0f seconds' % self.eat_time)
Но, увы, теперь разрешены только хорошие собаки:
# good dog
dog = Dog(4, 'woof')
dog.eat_and_speak() # <-- works fine
# bad dog
dog = Dog('four', 'woof')
dog.eat_and_speak() # <-- raises ValueError (as 'four' isn't float)
Плохая собака выдает ошибку значения, поскольку у нее больше нет декоратора.
Одним из способов решения этой проблемы является использование частного метода с декоратором:
class Animal:
def __init__(self, eat_time, animal_sound):
self.sound = animal_sound
self.eat_time = eat_time
def eat(self):
print('Eats for %0.0f seconds' % self.eat_time)
def speak(self):
print(self.sound)
@handleError
def _eat(self):
self.eat()
def eat_and_speak(self):
self._eat()
self.speak()
Теперь разрешено обрабатывать исключение для следующей плохой собаки:
class Dog(Animal):
def eat(self):
print('Dog eats for %0.0f seconds' % self.eat_time)
dog = Dog('four', 'woof')
dog.eat_and_speak()
> 'woof'
Однако это кажется хакерским и безобразным.
Вопрос: как классам лучше всего наследовать обработку ошибок от абстрактного класса?
Примечание. Реальный вариант использования - для некоторого стандартного кода скребка, где некоторые методы, как ожидается, будут иметь ошибки, но скребок должен продолжаться, возвращая None. Владельцы отдельных скребков унаследуют от этого класса, который, мы надеемся, справится с этим для них.