Python是否有类似于Perl 5.10的“state”变量?

9
在 Perl 5.10 中,我可以这样说:
sub foo () {
  state $x = 1;
  say $x++;
}

foo();
foo();
foo();

...并且它将打印出:

1
2
3

Python有类似这样的东西吗?

2
对于Python的粉丝们,请注意:状态变量不仅用于Perl中的生成器。它们是一种方便的封装信息的方法,需要在其生命周期内保持某些函数的状态。 :) - Robert P
11个回答

18

在这种情况下,使用类可能更加合适(通常在涉及"状态"的任何事情上都更加合适):

class Stateful(object):

    def __init__(self):
        self.state_var = 0

    def __call__(self):
        self.state_var = self.state_var + 1
        print self.state_var

foo = Stateful()
foo()
foo()

只是在示例中尝试明确表达。 - Ali Afshar
1
这对于如此简单的事情来说相当繁重,你觉得呢? - tchrist
2
tchrist: “heavy” 是相对的。大多数使用情况下,人们永远不会注意到重量上的差异。 - Ali Afshar

12

最接近的类比可能是将值附加到函数本身。

def foo():
    foo.bar = foo.bar + 1

foo.bar = 0

foo()
foo()
foo()

print foo.bar # prints 3

在Python中,虽然实际上你从来不需要它们,但对于静态变量的+1习语。 - bobince
2
你不是刚刚创建了一个非私有变量吗!?哦,我的天! - tchrist
1
我喜欢 Python 粉丝通常将任何未直接包含的语言特性称为“实践中不需要的”,无论该特性是什么。 :) - Robert P
任何编程语言中都有很多“不必要”的东西。但如果它能帮助你编写正确的程序,那就是一件好事。Perl的状态变量就可以做到这一点。 - John Deighan

9

如果您不确定这是否是您要寻找的,那么Python具有生成器函数,它们不会直接返回值,而是生成一个生成器对象,每次生成一个新值。

def gen():
   x = 10
   while True:
      yield x
      x += 1

用法:

>>> a = gen()
>>> a.next()
10
>>> a.next()
11
>>> a.next()
12
>>> a.next()
13
>>> 

点击这里了解更多关于yield的解释:
Python中的“yield”关键字是什么意思?


9

5

以下是在Python中实现闭包的一种方法:

def outer():
    a = [4]
    def inner():
        print a[0]
        a[0] = a[0] + 1
    return inner

fn = outer()
fn() # => 4
fn() # => 5
fn() # => 6

我直接从一篇Python邮件列表的帖子中借鉴了这个例子。


3

我并不是在推荐这个,只是出于好玩:

def foo(var=[1]):
    print var[0]
    var[0] += 1

这是因为Python中的可变默认参数机制起作用。

2

是的,不过您必须在foo中使用前先声明全局变量:

x = 0

def foo():
    global x
    x += 1
    print x

foo()
foo()
foo()

编辑:回应评论,确实python没有函数内部的静态变量。需要注意的是,这个例子中的x只对模块的其余部分公开为全局变量。例如,假设上面的代码在test.py中。现在假设你编写了以下模块:

from test import foo
x = 100
foo()
foo()

输出结果只会是12,而不是101102


2
不同,因为变量没有在函数内部作用域中 - 它暴露给代码的其余部分。 - Arkady
是的,你说得对。Python没有静态变量,所以这是唯一的方法,而不必走类路线。但是,请注意,这只会作为全局变量暴露给模块的其他部分。 - Jarret Hardie
1
状态变量是词法作用域的,而不是全局的(这也是它们的主要优点,类似于全局变量“记住”值但没有实际全局变量的缺点)。 - Adam Kennedy
1
Python 缺乏对正确作用域的支持,这难道不是它的致命伤之一吗?在 Python 3000 中会修复这个问题吗? - tchrist

2
您也可以使用类似于以下的内容:
def static_num2():
    k = 0
    while True:
        k += 1
        yield k

static = static_num2().next

for i in range(0,10) :
    print static()

为避免全局变量。从此链接中提取有关相同问题的内容。


另外,在相当近期的Python中,您可以执行static= itertools.count().next并删除static_num2定义。 - tzot

2
首选方法是使用classgeneratoryield)。
为了完整起见,这里是Python 3.x下带闭包的变体:
>>> def make_foo():
...     x = 1
...     def foo():
...         nonlocal x
...         print(x)
...         x += 1
...     return foo
...
>>> foo = make_foo()
>>> foo()
1
>>> foo()
2
>>> foo()
3

对于所有 Python ≥2.5 版本:import itertools; foo = itertools.count(1).next - tzot
@ΤΖΩΤΖΙΟΥ:错误。注意:这里有一个“print”。无论如何,目的是演示如何绑定非本地名称。 - jfs

1

这里有另一种非常便宜的方法来实现它,它是Tryiptich答案的变体,但使用装饰器。

def static_var( name, value ):
    def dec( function ):
        setattr( function, name, value )
        return function
    return dec


@static_var( 'counter', 0 )
def counting_function():
    counting_function.counter = counting_function.counter + 1
    print counting_function.counter



"""
>>> counting_function()
1
>>> counting_function()
2
>>> counting_function()
3
>>> counting_function()
4
>>> counting_function()
5
>>> 
"""    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接