#python #class #inheritance #decorator
#python #класс #наследование #декоратор
Вопрос:
Я хочу создать класс для наследования в другом месте и использовать декоратор для хранения определенных методов в атрибуте.
Я попробовал следующий декоратор
def filtermethod(f):
def wrapped(self, *args, **kwargs):
self.methods.append(f)
return f(self, *args, **kwargs)
return wrapped
и определите классы с помощью
class foo:
def __init__(self):
self.methods = []
def apply_filter(self, x):
for fun in self.methods:
x = fun(x)
return x
class foo2(foo):
@filtermethod
def method1(self, x):
return [i for i in x if i > 5]
@filtermethod
def method2(self, x):
return [i for i in x if i < 18]
и протестируйте класс с помощью следующего
foo2().apply_filter([1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9])
и ожидайте, что все оформленные функции будут применены к аргументу, но я вижу
[1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9]
вместо
[7,13,9]
В принципе, я хочу добавить каждую функцию, которая украшена @filtermethod
атрибутом self.methods
, (для последовательного применения с self.apply_filter
), но я просто не могу.
Есть какие-либо подсказки?
Комментарии:
1. единственный раз, когда вы добавляете обернутый метод в
self.methods
, это когда вы на самом деле вызываете обернутый метод — например,method1
илиmethod2
.
Ответ №1:
filtermethod
сам вызывается по мере создания класса. следовательно, вам нужно что-то сделать на уровне класса.
ниже я изменил ваш код, чтобы показать, как это может работать. все, что я делаю, это то, что декоратор помечает функции, которые он оборачивает, переменной, которая __init_subclass__
выбирает и добавляет к _methods
(как несвязанный метод).
def filtermethod(f):
f._is_filter_method = True
return f
class foo:
def __init_subclass__(cls, **kwargs):
funcs = (getattr(cls, v) for v in dir(cls))
cls._methods = [f for f in funcs if hasattr(f, '_is_filter_method')]
def apply_filter(self, x):
for fun in self._methods:
x = fun(self, x)
return x
class foo2(foo):
@filtermethod
def method1(self, x):
return [i for i in x if i > 5]
@filtermethod
def method2(self, x):
return [i for i in x if i < 18]
class foo3(foo2):
@filtermethod
def method1(self, x):
return [i for i in x if i == 4]
редактировать: исправлено для переопределения
Комментарии:
1. Спасибо. Могу ли я перезаписать на
method1
с новым дочерним классомfoo3(foo2)
?2. с новым классом
foo3(foo2)
я не смог перезаписатьmethod1
должным образом.method2
Либо стал неактивным, либо оба метода вообще не работали3. @mccandar попробуйте это сейчас — исправил
Ответ №2:
Если вы запустите свой код и посмотрите на список методов, вы заметите, что он пуст. Декоратор будет украшать метод при определении, а не при инициализации экземпляра. Один из способов увидеть, как это происходит, — поместить некоторые отпечатки в декоратор и запустить только определение класса.
Чтобы достичь желаемого, одним из способов было бы использовать переменную класса в качестве реестра методов и слегка изменить декоратор как:
def filtermethod(registry):
def inner(f):
registry.append(f)
def wrapped(*args, **kwargs):
return f(*args, **kwargs)
return wrapped
return inner
class foo:
methods = []
def apply_filter(self, x):
for fun in foo.methods:
x = fun(self, x)
return x
class foo2(foo):
@filtermethod(foo.methods)
def method1(self, x):
return [i for i in x if i > 5]
@filtermethod(foo.methods)
def method2(self, x):
return [i for i in x if i < 18]
Обратите внимание, что self
передается в качестве аргумента в apply_filter
методе.
Когда вы запускаете:
foo2().apply_filter([1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9])
Вы получите:
[7, 13, 9]