这个Python“静态变量”黑客可以使用吗?

时间:2016-04-01 04:00:42

标签: python static

一个经常被问到的问题是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

有什么理由我应该避免这种方法吗?无论如何,它是如何工作的?

6 个答案:

答案 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中,使用生成器函数可能会更好。

  1. 它启用多个同步范围(每个生成器实例都可以拥有自己的foo.counter实例)。
  2. “静态”变量被正确封装在函数的范围内(foo.counter实际上在外部作用域(文件级作用域))。
  3. 以下是使用两个同步生成器的示例,每个生成器都有自己的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)