一个经常被问到的问题是Python中的函数内部是否存在等效的静态变量。有许多答案,例如使用嵌套函数,装饰器等创建包装类。
我找到的最优雅的解决方案之一是{{3}},我稍作修改:
def foo():
# see if foo.counter already exists
try: test = foo.counter
# if not, initialize it to whatever
except AttributeError: foo.counter = 0
# do stuff with foo.counter
.....
.....
示例:
static.py
def foo(x):
# see if foo.counter already exists
try: test = foo.counter
# if not, initialize it to whatever
except AttributeError: foo.counter = 0
foo.counter += x
print(foo.counter)
for i in range(10):
foo(i)
输出
$ python static.py
0
1
3
6
10
15
21
28
36
45
有什么理由我应该避免这种方法吗?无论如何,它是如何工作的?
答案 0 :(得分:2)
它工作因为函数的名称只是本地作用域中的另一个条目,函数是像Python中其他所有对象的对象,并且可以在其上设置任意属性:
def foo():
# The foo function object has already been fully constructed
# by the time we get into our `try`
try: test = foo.counter # Only run when foo is invoked
except AttributeError: foo.counter = 0
foo.counter += 1
if hasattr(foo, 'counter'):
print('Foo has a counter attribute')
else:
# This prints out - we've parsed `foo` but not executed it yet
print('Foo.counter does not exist yet')
# Now, we invoke foo
foo()
if hasattr(foo, 'counter'):
# And from now on (until we remove the attribute)
# this test will always succeed because we've added the counter
# attribute to the function foo.
print('Foo has a counter attribute')
else:
print('Foo.counter does not exist yet') # No longer true
答案 1 :(得分:1)
为什么不这样:
def foo(x):
foo.counter += x
print(foo.counter)
foo.counter = 0 # init on module import
然后:
for i in range(10):
foo(i)
我使用py2.7,py3.4获得相同的输出。
答案 2 :(得分:1)
您所拥有的解决方案可以正常使用,但如果您在最优雅的解决方案之后,您可能更喜欢这个(根据您链接的答案之一进行调整):
def foo(x):
foo.counter = getattr(foo, 'counter', 0) + x
print(foo.counter)
for i in range(10):
foo(i)
它的工作方式基本相同,但getattr
会返回默认值(0),仅当foo
没有counter
属性时才会应用。
答案 3 :(得分:1)
在python中,使用生成器函数可能会更好。
foo.counter
实例)。foo.counter
实际上在外部作用域(文件级作用域))。以下是使用两个同步生成器的示例,每个生成器都有自己的counter
变量版本(“静态”变量不可用)。
def foo():
counter = 0
while True:
# You can yield None here if you don't want any value.
yield counter
counter += 1
gen1 = foo()
gen2 = foo()
gen1.next()
# 0
gen1.next()
# 1
gen2.next()
# 0
您可以为生成器提供一些初始值,也可以将数据发送回生成器。
def foo(x=0)
counter = x
val = 1
while True:
sent = (yield counter)
if sent is None:
counter += val
val = 1
else:
val = sent
gen1 = foo(3)
gen1.next()
# 3
gen1.send(3)
gen1.next()
# 6
gen1.next()
# 7
除了简单地迭代一个计数器,你可以做更多的事情。生成器是python中的一个强大工具,比简单的“静态”变量更灵活。
答案 4 :(得分:1)
我觉得对象正是你在这里寻找的东西。它是一些状态附加到一些使用和操纵该状态的动作(在这种情况下是一个动作)。那么为什么不呢:
class Foo(object):
def __init__(self, start=0):
self.counter = start
def foo(self, x):
self.counter += x
print(self.counter)
foo = Foo()
for i in range(10):
foo.foo(i)
正如其他人所说,如果你真的想避免上课,你可以。函数已经是一个对象,可以添加任何属性,就像任何普通对象一样。但为什么你真的想要呢?我知道为单个函数编写一个类感觉有点像矫枉过正,但是你已经声明你的实际代码有各种各样的操作随之而来。如果没有看到各种各样的操作,看起来你在这里使用类似乎有合理的理由。
答案 5 :(得分:0)
如果foo.counter在函数中找不到一个问题,那么你可能会遇到一些问题。例如,以下返回101而不是1
class Bar():
counter = 100
class Hoo():
def foo(x):
# see if foo.counter already exists
try: test = foo.counter
# if not, initialize it to whatever
except AttributeError: foo.counter = 0
foo.counter += x
print(foo.counter)
# make an object called foo that has an attribute counter
foo = Bar()
# call the static foo function
Hoo.foo(1)