如何对python中的子例程应用decorator

2024-09-30 00:31:44 发布

您现在位置:Python中文网/ 问答频道 /正文

我想修改我的几个函数的行为方式,所以我想到了装饰器的使用。例如,假设我有一个批量数据获取函数takeData(s)

def takeData(s):
    takeDataSet_1(s)
    takeDataSet_2(s)
    .
    .
    .

我可能想做的一件简单的事情是在每次takeDataSet函数调用之前更新参数dict s。所以有效的代码应该是这样的:

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)

有没有一种方法可以像decorator那样控制递归的深度?因此,如果takeDataSet\u 1(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)

Tags: 数据方法函数代码参数def方式装饰
2条回答

不,装饰者包装一个函数,这意味着他们可以在函数前后添加自己的行为。它们不能像您的示例那样更改函数“中间”发生的事情(更不用说像递归示例那样更改该函数调用的其他函数中间发生的事情)。你知道吗

您可以创建一个类似decorator的函数,它接受takeData_*函数作为参数并进行更新,因此您可以执行以下操作:

def updateAndCall(func, params):
    s = updateParams(params)
    func(s)

def takeData(s):
    updateAndCall(takeData_1, s)
    updateAndCall(takeData_2, s)

然而,这是否有用取决于各种功能之间的相互作用。特别是,使用这种方法,每次“更新”都会发生在原始的s;更新不会随着第二次调用的更新s而累积

非常有趣的问题。要实现这一点,您需要深入研究函数对象(不管怎样,都不要使用evalexecast之类的东西)。你知道吗

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()')

相关问题 更多 >

    热门问题