Я хотел продемонстрировать полезность декораторов в python для некоторых людей и провалился на простом примере: рассмотрим две функции (для простоты без аргументов) f
и g
. Можно определить их сумму f + g как функцию, которая возвращает f() + g(). Конечно, добавление, вычитание и т. Д. Функций вообще не определено. Но легко написать декоратор, который преобразует каждую функцию в добавочную функцию.Почему эта реализация python для «работоспособной функции» не работает?
Теперь я хотел бы иметь декоратор, который преобразует любую функцию в «действующую» функцию, то есть функцию, которая ведет себя описанным способом для любого оператора в стандартном модуле operator
. Моя реализация выглядит следующим образом:
import operator
class function(object):
def __init__(self, f):
self.f = f
def __call__(self):
return self.f()
def op_to_function_op(op):
def function_op(self, operand):
def f():
return op(self(), operand())
return function(f)
return function_op
binary_op_names = ['__add__', '__and__', '__div__', '__eq__', '__floordiv__', '__ge__', '__gt__', '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__or__', '__pow__', '__sub__', '__truediv__', '__xor__']
for name in binary_op_names:
type.__setattr__(function, name, op_to_function_op(getattr(operator, name)))
Давайте выполнить небольшой тест, чтобы увидеть, если он работает:
@function
def a():
return 4
def b():
return 7
c = a + b
print c()
print c() == operator.__add__(4, 7)
Выход:
11
True
Это последняя версия, которую я получил после того, как некоторые эксперименты , Теперь давайте сделаем два маленьких, несоответствующие изменения, чтобы посмотреть, что я пытался раньше:
Первого: В определении binary_op_names
, изменить квадратные скобки в круглые скобки. Внезапно появляется (для меня) полностью несвязанное сообщение об ошибке:
Traceback (most recent call last):
File "example.py", line 30, in <module>
c = a + b
TypeError: unsupported operand type(s) for +: 'function' and 'function'
Откуда это?
Второй: Написать op_to_function_op
как лямбда-выражения:
op = getattr(operator, name)
type.__setattr__(function, name, lambda self, other: function(lambda: op(self(), other())))
Perform немного более активное участие тестовый пример:
@function
def a():
return 4
def b():
return 7
c = a + b
print c()
print c() == operator.__add__(4, 7)
print c() == operator.__xor__(4, 7)
Выход:
3
False
True
Это выглядит для меня как утечка области, но снова я Не понимаю, почему это происходит.
типа .__ setattr__, кажется, сделать что-то плохое .. Дон Не знаю, что. Список для кортежей действительно странный. Если вы измените тип.__setattr__ to setattr все, кажется, работает. – mkorpela
Типичный декоратор 'memoize', примененный к последовательности фибоначчи, и другие проблемы, связанные с кешем, могут быть лучшим приложением для демонстрации декораторов. – Daenyth
Проблема с кортежем кажется отвратительной ошибкой. Я вижу такое же поведение. Абсолютно все о 'name' проверяется, но' type .__ setattr__' не работает. – Nate