Я понимаю, что функции могут иметь атрибуты. Поэтому я могу сделать следующее:
def myfunc():
myfunc.attribute += 1
print(myfunc.attribute)
myfunc.attribute = 1
Можно ли каким-либо образом заставить такую функцию вести себя так, как если бы она была экземпляром? Например, я хотел бы иметь возможность сделать что-то вроде этого:
x = clever_wrapper(myfunc)
y = clever_wrapper(myfunc)
x.attribute = 5
y.attribute = 9
x() # I want this to print 6 (from the 5 plus increment)
y() # I want this to print 10 (from the 9 plus increment)
В существующем виде существует только один «экземпляр» функции, поэтому attribute
существует только один раз. Изменение его либо x
, либо y
изменяет то же значение. Я бы хотел, чтобы у каждого из них был свой attribute
. Можно ли вообще это сделать? Если да, можете ли вы привести простой, функциональный пример?
Важно, чтобы я мог получить доступ к attribute
изнутри функции, но иметь значение attribute
в зависимости от того, какой "экземпляр" функции вызывается. По сути, я хотел бы использовать attribute
, как если бы это был другой параметр функции (чтобы он мог изменить поведение функции), но не передавать его. (Предположим, что сигнатура функции была исправлена так, Я не могу изменить список параметров.) Но мне нужно иметь возможность установить различные значения для attribute
и затем вызывать функции по порядку. Я надеюсь, что это имеет смысл.
Основные ответы, кажется, говорят сделать что-то вроде этого:
class wrapper(object):
def __init__(self, target):
self.target = target
def __call__(self, *args, **kwargs):
return self.target(*args, **kwargs)
def test(a):
return a + test.attribute
x = wrapper(test)
y = wrapper(test)
x.attribute = 2
y.attribute = 3
print(x.attribute)
print(y.attribute)
print(x(3))
print(y(7))
Но это не работает. Возможно, я сделал это неправильно, но там написано, что test
не имеет attribute
. (Я предполагаю, что это потому, что wrapper
на самом деле имеет атрибут.)
Причина, по которой мне это нужно, в том, что у меня есть библиотека, которая ожидает функцию с определенной сигнатурой. Можно поместить эти функции в конвейер, чтобы они вызывались по порядку. Я хотел бы передать несколько версий одной и той же функции, но изменить их поведение в зависимости от значения атрибута. Поэтому я хотел бы иметь возможность добавлять x
и y
в конвейер, в отличие от необходимости реализовывать функцию test1
и функцию test2
, которые оба выполняют почти одно и то же (за исключением значение атрибута).