Squeak.ru - шаблоны программирования

Python: украшение метода класса, который должен быть перезаписан при наследовании

Скажем, у меня есть базовый класс:

class Task:
    def run(self):
        #override this!

Теперь я хочу, чтобы другие создали подкласс Task и переопределили метод run():

class MyTask(Task):
    def run(self):
        #successful override!

Однако проблема в том, что существует логика, которая должна выполняться до и после метода run() каждого класса, являющегося подклассом Task.

Похоже, что один из способов сделать это — определить другой метод в базовом классе, который затем вызывает метод run(). Однако я хотел спросить, есть ли способ добиться этого с помощью декораторов? Каким был бы самый пифонический способ сделать это?


  • Предположительно другой процесс будет импортировать подклассы Task и вызывать метод run(). Не уверены, что это ответ на ваш вопрос? 17.09.2013
  • Почему бы вам просто не переопределить run, но сделать так, чтобы API вызывал какой-то другой метод, который выполняет действия до/после и вызывает сам run? 18.09.2013
  • Почему бы просто не сделать run() из Task тем, что он должен делать, и вызвать функцию, которая переопределена в производном классе, например do_run()? Вы по-прежнему просите производный класс реализовать только одну функцию. 18.09.2013
  • Да, это все определенно жизнеспособные варианты для моего варианта использования. В основном мне было интересно, есть ли какая-то магия декоратора или стандартный способ обработки такого рода вещей. 18.09.2013

Ответы:


1

Как было предложено в комментариях, лучше всего было бы позволить подклассам переопределить хук вместо самого run:

class Task(object):
    def run(self):
        # before 
        self.do_run()
        # after

class MyTask(Task):
    def do_run(self):
        ...

task = MyTask()
task.run()

Однако это один из способов, который можно сделать с помощью декоратора класса:

def decorate_run(cls):
    run = getattr(cls, 'run')
    def new_run(self):
        print('before')
        run(self)
        print('after')
    setattr(cls, 'run', new_run)
    return cls


class Task(object): pass

@decorate_run
class MyTask(Task):
    def run(self):
        pass

task = MyTask()
task.run()

# prints:
# before
# after

Другим способом было бы использование метакласса. Преимущество использования метакласса состоит в том, что подклассы не нужно декорировать. Task можно сделать экземпляром метакласса, и тогда все подклассы Task автоматически наследуют метакласс.

class MetaTask(type):
    def __init__(cls, name, bases, clsdict):
        if 'run' in clsdict:
            def new_run(self):
                print('before')
                clsdict['run'](self)
                print('after')
            setattr(cls, 'run', new_run)

class Task(object, metaclass=MetaTask):
    # For Python2: remove metaclass=MetaTask above and uncomment below:
    # __metaclass__ = MetaTask
    pass

class MyTask(Task):
    def run(self):
        #successful override!
        pass

task = MyTask()
task.run()
17.09.2013
  • Спасибо за волшебный совет метакласса, это то, о чем я действительно задавался вопросом. Похоже, я, вероятно, заставлю других переопределить хук, но очень полезно знать, что метаклассы — это вариант. Спасибо! 18.09.2013
  • Я всегда видел, как __new__ используется в метаклассах, а не __init__ - это получается намного чище. 18.09.2013
  • Причина, по которой __new__ существует в Python, заключается в том, что неизменяемые типы могут сохранять свою неизменность, позволяя создавать подклассы. (Если бы не было __new__, а неизменяемые использовали __init__, тогда вы могли бы изменить значение неизменяемых, вызывая такие вещи, как math.pi.__init__(3.0)!) Поэтому я считаю, что вы должны зарезервировать __new__ только для тех ситуаций, когда вы хотите, чтобы экземпляры были неизменяемыми. 18.09.2013
  • Пример метакласса - это именно то, что я ищу, но, похоже, он не работает должным образом. Я не вижу, печатается ли before или after. Что мне не хватает? 05.09.2017
  • @Jørgen: синтаксис объявления метакласса изменился между Python2 и Python3. Если вы используете Python3, добавьте metaclass=MetaTask в базовое объявление класса. Я отредактировал сообщение выше, чтобы показать, что я имею в виду. 05.09.2017
  • @unutbu: Спасибо за быстрый ответ! 05.09.2017
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...