如何将装饰器应用于 python 中的子例程
How to apply a decorator to subroutines in python
我想修改我的几个函数的行为方式,所以想到了装饰器的使用。例如,假设我有一个批量数据获取功能 takeData(s)
:
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
.
.
.
我可能想做的一件简单的事情是在每次调用 takeDataSet 函数之前更新参数 dict 。这样有效的代码会更像这样:
def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
.
.
.
有没有一种方法可以用装饰器做到这一点,这样我的代码看起来更像
@takeDataWithUpdatedParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
有没有办法控制这种装饰器的递归深度?这样如果 takeDataSet_1(s) 有它自己的 s 的子程序可以在它们之间更新,如:
@recursiveUpdateParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
def takeDataSet_1(s):
takeData_a(s)
takeData_b(s)
作为
执行
def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
def takeDataSet_1(s):
s = updateParams(s)
takeData_a(s)
s = updateParams(s)
takeData_b(s)
没有。装饰器包装一个函数,这意味着它们可以在函数前后添加自己的行为。他们不能像您的示例那样更改函数 "in the middle" 发生的事情(更不用说在该函数调用的其他函数中间发生的事情,如您的递归示例)。
您可以创建一个类似装饰器的函数,它接受您的 takeData_*
函数作为参数并进行更新,因此您可以执行如下操作:
def updateAndCall(func, params):
s = updateParams(params)
func(s)
def takeData(s):
updateAndCall(takeData_1, s)
updateAndCall(takeData_2, s)
但是,这是否有用取决于各种功能之间的交互。特别是,使用这种方法,每个 "update" 都发生在原始 s
上;更新不会与更新的 s
一起为第二次调用再次更新,等等
非常有趣的问题。为此,您需要深入研究函数对象(无论如何,没有 eval
、exec
或 ast
东西)。
def create_closure(objs):
creat_cell = lambda x: (lambda: x).__closure__[0]
return tuple(create_cell(obj) for obj in objs)
def hijack(mapper):
from types import FunctionType
def decorator(f):
globals_ = {k: mapper(v) for k, v in f.__globals__.items()}
closure_ = f.__closure__
if closure_:
closure_ = create_closure(i.cell_contents for i in closure_)
return (lambda *arg, **kwarg:
FunctionType(f.__code__, globals_, f.__name__,
f.__defaults__, closure_)(*arg, **kwarg))
return decorator
测试:
x = 'x'
y = 'y'
@hijack(lambda obj: 'hijacked!' if obj is x else obj)
def f():
return (x, y)
f()
输出:
('hijacked!', 'y')
终于解决了原问题:
x = lambda: 'x()'
y = lambda: 'y()'
mydecorator = lambda f: lambda *arg, **kwarg: f(*arg, **kwarg) + ' decorated!'
targets = {id(x): mydecorator(x)}
def mapper(obj):
if id(obj) in targets:
return targets[id(obj)]
else:
return obj
@hijack(mapper)
def f():
return (x(), y())
f()
输出:
('x() decorated!', 'y()')
我想修改我的几个函数的行为方式,所以想到了装饰器的使用。例如,假设我有一个批量数据获取功能 takeData(s)
:
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
.
.
.
我可能想做的一件简单的事情是在每次调用 takeDataSet 函数之前更新参数 dict 。这样有效的代码会更像这样:
def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
.
.
.
有没有一种方法可以用装饰器做到这一点,这样我的代码看起来更像
@takeDataWithUpdatedParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
有没有办法控制这种装饰器的递归深度?这样如果 takeDataSet_1(s) 有它自己的 s 的子程序可以在它们之间更新,如:
@recursiveUpdateParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
def takeDataSet_1(s):
takeData_a(s)
takeData_b(s)
作为
执行def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
def takeDataSet_1(s):
s = updateParams(s)
takeData_a(s)
s = updateParams(s)
takeData_b(s)
没有。装饰器包装一个函数,这意味着它们可以在函数前后添加自己的行为。他们不能像您的示例那样更改函数 "in the middle" 发生的事情(更不用说在该函数调用的其他函数中间发生的事情,如您的递归示例)。
您可以创建一个类似装饰器的函数,它接受您的 takeData_*
函数作为参数并进行更新,因此您可以执行如下操作:
def updateAndCall(func, params):
s = updateParams(params)
func(s)
def takeData(s):
updateAndCall(takeData_1, s)
updateAndCall(takeData_2, s)
但是,这是否有用取决于各种功能之间的交互。特别是,使用这种方法,每个 "update" 都发生在原始 s
上;更新不会与更新的 s
一起为第二次调用再次更新,等等
非常有趣的问题。为此,您需要深入研究函数对象(无论如何,没有 eval
、exec
或 ast
东西)。
def create_closure(objs):
creat_cell = lambda x: (lambda: x).__closure__[0]
return tuple(create_cell(obj) for obj in objs)
def hijack(mapper):
from types import FunctionType
def decorator(f):
globals_ = {k: mapper(v) for k, v in f.__globals__.items()}
closure_ = f.__closure__
if closure_:
closure_ = create_closure(i.cell_contents for i in closure_)
return (lambda *arg, **kwarg:
FunctionType(f.__code__, globals_, f.__name__,
f.__defaults__, closure_)(*arg, **kwarg))
return decorator
测试:
x = 'x'
y = 'y'
@hijack(lambda obj: 'hijacked!' if obj is x else obj)
def f():
return (x, y)
f()
输出:
('hijacked!', 'y')
终于解决了原问题:
x = lambda: 'x()'
y = lambda: 'y()'
mydecorator = lambda f: lambda *arg, **kwarg: f(*arg, **kwarg) + ' decorated!'
targets = {id(x): mydecorator(x)}
def mapper(obj):
if id(obj) in targets:
return targets[id(obj)]
else:
return obj
@hijack(mapper)
def f():
return (x(), y())
f()
输出:
('x() decorated!', 'y()')