Я хотел продемонстрировать полезность декораторов для некоторых людей на python и потерпел неудачу на простом примере: рассмотрим две функции (для простоты без аргументов) f
и g
.Можно определить их сумму f + g как функцию, которая возвращает f () + g ().Конечно, сложение, вычитание и т. Д. Функций вообще не определяется.Но легко написать декоратор, который превращает каждую функцию в добавляемую функцию.
Теперь я хотел бы иметь декоратор, который преобразует любую функцию в «работоспособную» функцию, то есть функцию, которая ведет себя вописанный способ для любого оператора в стандартном модуле 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'
Откуда это происходит?
Second : записать op_to_function_op
каклямбда-выражение:
op = getattr(operator, name)
type.__setattr__(function, name, lambda self, other: function(lambda: op(self(), other())))
Выполните несколько более сложный тестовый пример:
@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
Это выглядит для меня как утечка области, но опятьЯ не понимаю, почему это происходит.