Избавляемся от явного супер - PullRequest
0 голосов
/ 21 сентября 2019

Я хотел бы реализовать что-то вроде этого

def after(f):
    def inner(*args, **kwargs):
        super().f(*args, **kwargs)
        f(*args, **kwargs)
    return inner


class A:
  def f(self):
    print ('hello')


class B(A):

  @after
  def f(self):
    print ('world')

b = B()
b.f()

, то есть я хотел бы избавиться от явного super в некоторых моих классах и заменить его на @before / @afterdecorator (возможно, с параметрами).

То есть в этом примере я бы хотел, чтобы hello world был напечатан.

Идея состоит в том, чтобы повысить читаемость кода, как в некоторыхклассы, которые я часто использую множественное наследование, поэтому я часто переопределяю методы и часто вынужден использовать super().

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

есть ли способ сделать это без ущерба для производительности?

1 Ответ

1 голос
/ 21 сентября 2019

Вы можете заставить ваш декоратор работать, вам просто нужно сделать его дескриптором, а не функцией.Вам нужно реализовать метод __set_name__, чтобы получить ссылку на класс, к которому вы были добавлены.С помощью ссылки на класс вы можете сделать вызов с двумя аргументами super:

import functools

class after:
    def __init__(self, method):
        self.method = method

    def __set_name__(self, owner, name):
        self.owner = owner
        self.name = name                 # using self.method.__name__ might be better?

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return functools.partial(self, instance)

    def __call__(self, instance, *args, **kwargs):
        assert(self.owner is not None and self.name is not None)
        getattr(super(self.owner, instance), self.name)(*args, **kwargs)
        return self.method(instance, *args, **kwargs)

Вы также можете сделать before, что будет почти таким же, только с последними двумя строками вобратный порядок (и некоторая путаница для обработки возвращаемого значения).

Я бы отметил, что этот декоратор в общем-то менее полезен, чем обычный вызов super, поскольку вы не можете с пользой взаимодействовать сзначение, возвращаемое переопределенным методом, или измените передаваемые ему аргументы.Не существует before или after оформленного метода, который может копировать эти классы:

class Foo:
    def foo(self, x, y):
        return x + y

class Bar(Foo):
    def foo(self, x, y, z):
        return super().foo(x//2, y+1) * z
...