当前位置:  开发笔记 > 编程语言 > 正文

函数内部的Python等价的静态变量是什么?

如何解决《函数内部的Python等价的静态变量是什么?》经验,为你挑选了13个好方法。

什么是这个C/C++代码的惯用Python等价物?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

具体来说,如何在功能级别实现静态成员,而不是类级别?将函数放入类中会改变什么吗?



1> Claudiu..:

有点逆转,但这应该工作:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
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.不幸的是,它仍然需要你使用前缀.


编辑(感谢ony):这看起来更好:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

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


很抱歉挖掘它,但我宁愿把'if'反击"不在foo .__ dict__:foo.counter = 0`作为`foo()`的第一行.这有助于避免函数外的代码.不确定这是否有可能在2008年.PS在寻找创建静态函数变量的可能性时找到了这个答案,所以这个线程仍然"活着":)
只有一个foo实例 - 这个功能.所有调用都访问同一个变量.
@binaryLV:我可能更喜欢第一种方法.第一种方法的问题是`foo`和`foo.counter =`密切相关并不是很明显.但是,我最终更喜欢装饰器方法,因为没有办法不会调用装饰器,它在语义上更明显它的作用(`@static_var("counter",0)`更容易开启并且对我的眼睛更有意义比'if'反击"不在foo .__ dict__:foo.counter = 0`,特别是在后者你必须使用可能改变的函数名称(两次).
@lpapp:这取决于静态变量的作用点.我一直认为它是多个函数调用的相同值,这确实满足.我从来没有把它当作变量隐藏,这不是,正如你所说的那样.
我在早些时候匆忙推翻了这个答案,但我认为这是误导,因为python不提供类似C/C++函数的静态变量.即使有了这个"解决方案",你也可以轻松访问函数外的"函数静态"变量,这有点难以理解.是的,我知道你也可以通过原始内存访问C/C++中的这些变量,但是,这个答案只是低声说它是一个真正的替代品,而它不是,因此它可能没有太多实际用途恕我直言.

2> vincent..:

您可以向函数添加属性,并将其用作静态变量.

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

或者,如果您不想在函数外部设置变量,则可以使用hasattr()以避免AttributeError异常:

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

无论如何,静态变量相当罕见,你应该为这个变量找到一个更好的位置,很可能是在一个类中.


`try:myfunc.counter + = 1; 除了AttributeError:myfunc.counter = 1`应该做同样的事情,而不是使用异常.
@Hack_Saw:嗯,这是Pythonic(更好地要求宽恕而不是许可).这实际上是在Python优化技术中推荐的,因为它节省了if的成本(尽管我不建议过早优化).关于特殊情况的规则:1.在某种意义上,失败是一种例外情况.它只发生过一次.我认为该规则是关于使用(即提高)例外.对于您期望工作但有备份计划的事情,这是一个例外,这在大多数语言中都很常见.
为什么不尝试而不是if语句?

3> ravwojdyla..:

人们还可以考虑:

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

推理:

多pythonic(ask for forgiveness not permission)

使用异常(仅抛出一次)而不是if分支(想想StopIteration异常)


这是正确的解决方案,它应该是可接受的答案,因为初始化代码将在调用函数时运行,而不是在执行模块或导入某些内容时运行,如果您使用装饰器方法,则会出现这种情况.目前接受的答案.请参阅[Python装饰器功能执行](http://stackoverflow.com/questions/36435236/python-decorator-function-execution).如果你有一个巨大的库模块,那么每个装饰器都会被运行,包括那些你没有导入的函数.
我没有长时间使用Python,但这满足了该语言的隐含权限之一:**如果它不(非常)容易,那么你做错了**.
@MANU为此使用“ hasattr()”并不简单,而且效率也较低。
一个更简单的方法:`def fn():如果不是hasattr(fn,'c'):fn.c = 0``fn.c + = 1返回fn.c`

4> 小智..:

其他答案已经证明了你应该这样做的方式.这是你不应该这样做的方式:

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

默认值仅在首次计算函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来存储静态值.


为什么说您不应该那样做?对我来说看起来很合理!

5> Jonathan..:

很多人已经建议测试'hasattr',但有一个更简单的答案:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

没有尝试/除,没有测试hasattr,只有getattr默认.



6> daniels..:

Python没有静态变量,但您可以通过定义可调用的类对象然后将其用作函数来伪造它.另见这个答案.

class Foo(object):
  # Class variable, shared by all instances of this class
  counter = 0

  def __call__(self):
    Foo.counter += 1
    print Foo.counter

# Create an object instance of class "Foo," called "foo"
foo = Foo()

# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3

请注意,__call__使类(对象)的实例可以通过其自己的名称进行调用.这就是为什么调用foo()上面调用类的__call__方法.从文档:

通过__call__()在类中定义方法,可以使任意类的实例可调用.


函数已经是对象,所以这只是添加了一个不必要的层.

7> Riaz Rizvi..:

这是一个完全封装的版本,不需要外部初始化调用:

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

在Python中,函数是对象,我们可以通过特殊属性简单地向其添加或修补成员变量__dict__.内置vars()返回特殊属性__dict__.

编辑:注意,与备选try:except AttributeError答案不同,使用此方法,变量将始终为初始化后的代码逻辑做好准备.我认为try:except AttributeError以下的替代方案将减少DRY和/或具有尴尬的流程:

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

EDIT2:我只推荐上述方法,当从多个位置调用该函数时.相反,如果只在一个地方调用该函数,最好使用nonlocal:

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...


可能应该使用类似`try:mystaticfun.counter + = 10的东西,除了AttributeError:mystaticfun.counter = 0`
请使用“不在Y中的X”而不是“不在Y中的X”(或建议使用,如果您只是为了与“ hasattr”之间看起来更相似的比较而使用它)

8> gnud..:

使用生成器函数生成迭代器.

def foo_gen():
    n = 0
    while True:
        n+=1
        yield n

然后就像使用它一样

foo = foo_gen().next
for i in range(0,10):
    print foo()

如果你想要一个上限:

def foo_gen(limit=100000):
    n = 0
    while n < limit:
       n+=1
       yield n

如果迭代器终止(如上例所示),你也可以直接循环它,比如

for i in foo_gen(20):
    print i

当然,在这些简单的情况下,最好使用xrange :)

以下是yield语句的文档.



9> cbarrick..:

其他解决方案通常使用复杂的逻辑来将计数器属性添加到函数,以处理初始化。这不适用于新代码。

在Python 3中,正确的方法是使用以下nonlocal语句:

counter = 0
def foo():
    nonlocal counter
    counter += 1
    print(f'counter is {counter}')

有关声明的说明,请参见PEP 3104nonlocal



10> Giorgian Bor..:
def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

与上面的vincent代码非常相似,它将用作函数装饰器,并且必须使用函数名作为前缀来访问静态变量.这段代码的优点(尽管可以说任何人都可能足够聪明地弄明白)是你可以拥有多个静态变量并以更传统的方式初始化它们.



11> kdb..:

使用函数的属性作为静态变量有一些潜在的缺点:

每次要访问变量时,都必须写出函数的全名.

外部代码可以轻松访问变量并弄乱值.

第二个问题的惯用python可能是用一个前导下划线命名变量,表示它不是要访问的,同时在事后保持可访问性.

另一种方法是使用词法闭包的模式nonlocal,python 3中的关键字支持这种模式.

def make_counter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter
counter = make_counter()

可悲的是,我知道无法将此解决方案封装到装饰器中.



12> warvariuc..:

更具可读性,但更冗长(Python的Zen:显式优于隐式):

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

请参阅此处以了解其工作原理。



13> Dave..:
_counter = 0
def foo():
   global _counter
   _counter += 1
   print 'counter is', _counter

Python通常使用下划线来表示私有变量.C在函数内声明静态变量的唯一原因是将它隐藏在函数外部,这不是真正的惯用Python.

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有