Python 3.5 装饰器和函数字段

Posted

技术标签:

【中文标题】Python 3.5 装饰器和函数字段【英文标题】:Python 3.5 Decorators and function fields 【发布时间】:2019-07-09 14:19:00 【问题描述】:

我有以下sn-p的代码:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        return func(*args, **kwargs)
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

您能否向我解释一下为什么运行 f_out() 会引发:

AttributeError: 'function' object has no attribute 'var'

编辑

我必须详细说明,因为答案为我提供了替代方案,但这不适用于我想要的情况。给定以下 sn-p:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1
f_out()
print(f_out.var)

我得到输出:

0
1

为什么会这样?

【问题讨论】:

return func 应该是 return wrapped。这能解决吗? @RobinZigmond 你是对的,我会修复它,但是同样的错误 【参考方案1】:

正确的方法是返回被包装的函数,并在返回之前改变它:

def wrapper(func):
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs)
    wrapped.var = 0
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

你正确得到:

print(f_out())

给予

0

更新后的片段将var 属性更改了两次:

第一次在包装器中将原始函数的属性 设置为 0,并在调用原始函数后打印它 然后,当从包装器调用原始函数时,它会将引用为f_out 的函数的属性设置为1。但此时,引用为f_out 的函数是包装函数,不再是原来的函数。

因此,当您稍后打印 f_out.var 时,您会打印包装函数的属性,即 1。

这是一个稍微修改过的代码:

def wrapper(func):
    def wrapped(*args, **kwargs):
        wrapped.orig = func          # keeps a ref to the original function
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1

f_out()
print(f_out.var, f_out.orig.var)

打印出来

0
1 0

【讨论】:

这是真的,它有效,但为什么另一个无效?我更新了我的问题 好的,很有趣,谢谢分享,我在其他地方找不到我的问题的答案。我以某种方式理解您关于原始函数和引用函数的论点,但如果是这样,那么对原始函数的对象引用将在包装器中丢失,因此在函数调用后,包装器完全无法访问函数体内修改的字段。 . 这让我问,有没有办法对函数体内的函数字段进行更改,可以通过它的包装器看到,以便例如执行或不执行进一步的计算?【参考方案2】:

装饰器在你调用它时包装你的函数。 因此,当您调用f_out() 时,它会返回1。它是你调用的包装函数,而不是它的定义。

@wrapper
def f_out()

相等 wrapper(f_out) 当你调用它时。

当您尝试打印 f_out.var 时,它会从函数定义中返回值。

【讨论】:

【参考方案3】:

如果return func,你必须添加return wrapped 试试这个:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        return func(*args, **kwargs)
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

【讨论】:

如果您尝试拨打f_out()会发生什么?

以上是关于Python 3.5 装饰器和函数字段的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器和回调函数

Python 函数装饰器和闭包

学习python的第十五天(函数的装饰器,两层装饰器和三层装饰器)

python第二阶段第四天 装饰器和匿名函数

Python迭代器和生成器,装饰器

python 装饰器和property