【问题标题】:Python: wrapping recursive functionsPython:包装递归函数
【发布时间】:2012-05-13 18:37:25
【问题描述】:

如何包装递归函数,包括递归调用?例如,给定foowrap

def foo(x):
    return foo(x - 1) if x > 0 else 1

def wrap(f):
    def wrapped(*args, **kwargs):
        print "f was called"
        return f(*args, **kwargs)

    return wrapped

wrap(foo)(x) 只会在第一次调用时输出"f was called"。递归调用仍然地址foo()

我不介意猴子修补,或在内部进行探索。我不打算将此代码添加到下一个核弹头处理程序中,所以即使这是一个坏主意,我也想达到效果。

编辑:例如,修补foo.func_globals 以覆盖foo.__name__ 是否有效?如果总是这样,我应该注意什么副作用?

【问题讨论】:

    标签: python higher-order-functions


    【解决方案1】:

    如果您将包装函数用作装饰器,它会起作用。

    def wrap(f):
        def wrapped(*args, **kwargs):
            print "f was called"
            return f(*args, **kwargs)
    
        return wrapped
    
    @wrap
    def foo(x):
        return foo(x - 1) if x > 0 else 1
    

    原因是在您的示例中,您只调用了一次 wrap 函数的结果。如果您将其用作装饰器,它实际上会用装饰函数替换模块命名空间中 foo 的定义,因此其内部调用解析为包装版本。

    【讨论】:

    • 我怎么没想到呢?哈哈!极好的。谢谢。
    • 实际上在您的原始示例中,如果您使用 foo = wrap(foo) 然后调用 foo(5),它也会做同样的事情 :) 但是装饰器语法是更清晰的方法。
    • 这会起作用,但是有没有办法在不覆盖命名空间中的原始名称的情况下做到这一点? @brandizzi 没有忘记,接受之前有最短时间:)
    • 不,如果不覆盖命名空间中的原始名称,就无法做到这一点。您正在按名称调用该函数;要调用 wrap 函数而不是按该名称调用原始函数,则 wrap 函数必须替换命名空间中的原始函数。
    • @kindall 实际上,可以通过将包装器显式传递给foo 来完成。但是这个looks super-ugly!
    【解决方案2】:

    用类而不是函数来包装函数:

    >>> def foo(x):
    ...     return foo(x-1) if x > 0 else 1
    ...
    >>> class Wrap(object):
    ...     def __init__(self, f): self.f = f
    ...     def __call__(self, *args, **kwargs):
    ...         print "f called"
    ...         return self.f(*args, **kwargs)
    ...
    >>> foo = Wrap(foo)
    >>> foo(4)
    f called
    1
    

    【讨论】:

    • 这正是我想要的! xD
    • 不需要将函数包装在一个类中——函数装饰器也可以。它在 OP 示例中不起作用,因为应该将包装的函数绑定到原始名称,如 @Kamil 答案中所示。
    • 哦,哈哈,我刚刚重读了这个问题。我以为你希望包装函数只执行一次:P。是的,卡米尔的回答很适合。
    猜你喜欢
    • 2022-01-01
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多