带有 Python 装饰器的函数中的静态变量

static variable in a function with Python Decorator

我在 Python 中寻找有关静态值的信息。

我发现 this

def static_var(varname, value):
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

@static_var("counter", 0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

函数中的静态变量使用 python 装饰器。

装饰器(static_var)在返回函数(foo)装饰器之前初始化静态值(foo.counter)

所以我认为它不应该按预期工作,因为装饰器(static_var)每次调用foo时都会初始化foo.counter

因此,我认为如果 foo() 被调用两次,它应该打印 1 两次

foo()     
foo()     

但它打印 12,增加 foo.counter

为什么...?

为什么每次调用 foo 时 foo.counter 没有初始化为 0

因为:

def static_var(varname, value):
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate


@static_var("counter", 0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

相当于:

foo = static_var("counter", 0)(foo)

注意到 static_var() 实际上被调用了吗?现在遍历你的装饰器;实际上用 import pdb; pdb.set_trace()

逐步完成它

Decorators wrap other functions potentially either mutating them or returning new functions or both.

参见:Understanding Python Decorators in 12 easy steps

如果我们在您的装饰器中添加一些 print(s),观察会发生什么:

def static_var(varname, value):
    print "1"

    def decorate(func):
        print "2"
        setattr(func, varname, value)
        return func
    print "3"
    return decorate


@static_var("counter", 0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter


foo()
foo()

输出:

$ python foo.py
1
3
2
Counter is 1
Counter is 2

So as I said above; firstly static_var("counter", 0) is called; then the return of that (which returns a function) is called with foo as it's first argument which sets up the initial "counter" and returns the same argument (the function foo).

您看到的行为是正确的。装饰器仅在声明时执行一次,而不是在每次调用装饰函数时执行一次。因此 foo.counter 只被初始化为 0 一次。

想想什么是静态变量。通常(例如在 C 语言中)它是一个函数的局部变量,在对函数的调用中持续存在。通过使用恰当地命名为 static_var 的装饰器,这里实现了相同的效果,因此每次调用 foo()foo.counter 递增而不是重置是有道理的。

"foo.counter initialized to 0 every time when foo is called?"

简短回答:不,foo.counter 仅在调用 static_var 时设置为 0。

我想您可能对装饰器和创建的函数装饰器没有什么混淆。

@decorator
def func():
    pass

只是

的语法糖
func = decorator(func)

有了这个,我们就会知道您之前的代码等于:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

foo = static_val(foo, "counter", 0)

然后,当您调用 foo() 时,您调用的是新的 foo 函数,而不是 static_val,其中 counter 属性将被设置为 0。