Как наследовать обработку ошибок в классе Python - PullRequest
2 голосов
/ 04 апреля 2019

Я пишу некоторый шаблонный код, который имеет некоторые абстрактные классы, от которых наследуются другие. Не каждый метод должен запускаться, поэтому абстрактный класс обрабатывает исключения, используя @handleError в качестве декоратора.

Я буду использовать собак, чтобы проиллюстрировать (реальная проблема гораздо более запутанная)

  1. Вот декоратор обработки исключений (если не удается запустить, вернуть None)
def handleError(f):
    def handleProblems(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception:
            return None
    return handleProblems
  1. Класс животных, который будет унаследован от
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()

  1. Так что теперь и "хорошие собаки" и "испорченные собаки" бегут до конца. («плохая собака» просто скажет «гав», конечно)
# 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. Владельцы отдельных скребков унаследуют от этого класса, который, мы надеемся, справится с этим для них.

...