这个C / C ++代码的惯用Python是什么?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体来说,如何在功能级别实现静态成员,而不是类级别?将函数放入类中会改变什么吗?
答案 0 :(得分:601)
有点逆转,但这应该有效:
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
答案 1 :(得分:196)
您可以向函数添加属性,并将其用作静态变量。
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
无论如何,静态变量相当罕见,你应该为这个变量找到一个更好的位置,很可能是在一个类中。
答案 2 :(得分:169)
还可以考虑:
def foo():
try:
foo.counter += 1
except AttributeError:
foo.counter = 1
推理:
ask for forgiveness not permission
)if
分支(认为StopIteration例外)答案 3 :(得分:37)
其他答案已经证明了你应该这样做的方式。这是你不应该的方式:
>>> def foo(counter=[0]):
... counter[0] += 1
... print("Counter is %i." % counter[0]);
...
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
默认值仅在首次评估函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来存储静态值。
答案 4 :(得分:29)
很多人已经建议测试' hasattr'但有一个更简单的答案:
def func():
func.counter = getattr(func, 'counter', 0) + 1
没有尝试/除了,没有测试hasattr,只有getattr默认。
答案 5 :(得分:23)
Python没有静态变量,但您可以通过定义可调用的类对象然后将其用作函数来伪造它。 Also see this answer
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__
方法的原因。 From the documentation:
通过在类中定义
__call__()
方法,可以使任意类的实例可调用。
答案 6 :(得分:22)
这是一个完全封装的版本,不需要外部初始化调用:
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
替代品将更少干燥和/或具有尴尬的流程:
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))
...
答案 7 :(得分:11)
使用生成器函数生成迭代器。
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 statement的文档。
答案 8 :(得分:6)
将函数的属性用作静态变量有一些潜在的缺点:
第二个问题的惯用python可能是用一个前导下划线来命名变量,以表示它不是要访问的,同时在事后保持可访问性。
另一种选择是使用词法闭包的模式,python 3中的nonlocal
关键字支持这种模式。
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
可悲的是,我知道无法将此解决方案封装到装饰器中。
答案 9 :(得分:6)
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代码非常相似,它将用作函数装饰器,并且必须使用函数名作为前缀来访问静态变量。这段代码的优点(虽然不可否认任何人都可能很聪明地弄明白)是你可以拥有多个静态变量并以更传统的方式初始化它们。
答案 10 :(得分:6)
_counter = 0 def foo(): global _counter _counter += 1 print 'counter is', _counter
Python通常使用下划线来表示私有变量。 C在函数中声明静态变量的唯一原因是将它隐藏在函数之外,这不是真正的惯用Python。
答案 11 :(得分:5)
所有以前的解决方案都将计数器属性附加到函数,通常使用复杂的逻辑来处理初始化。这不适合新代码。
在Python 3中,正确的方法是使用nonlocal
语句:
counter = 0
def foo():
nonlocal counter
counter += 1
print(f'counter is {counter}')
有关nonlocal
声明的说明,请参阅PEP 3104。
答案 12 :(得分:4)
更具可读性,但更详细:
>>> def func(_static={'counter': 0}):
... _static['counter'] += 1
... print _static['counter']
...
>>> func()
1
>>> func()
2
>>>
答案 13 :(得分:2)
由this question提示,我是否可以提出另一种可能更好用的替代方案,对于这两种方法和功能看起来都是一样的:
@static_var2('seed',0)
def funccounter(statics, add=1):
statics.seed += add
return statics.seed
print funccounter() #1
print funccounter(add=2) #3
print funccounter() #4
class ACircle(object):
@static_var2('seed',0)
def counter(statics, self, add=1):
statics.seed += add
return statics.seed
c = ACircle()
print c.counter() #1
print c.counter(add=2) #3
print c.counter() #4
d = ACircle()
print d.counter() #5
print d.counter(add=2) #7
print d.counter() #8
如果您喜欢这种用法,请参阅以下内容:
class StaticMan(object):
def __init__(self):
self.__dict__['_d'] = {}
def __getattr__(self, name):
return self.__dict__['_d'][name]
def __getitem__(self, name):
return self.__dict__['_d'][name]
def __setattr__(self, name, val):
self.__dict__['_d'][name] = val
def __setitem__(self, name, val):
self.__dict__['_d'][name] = val
def static_var2(name, val):
def decorator(original):
if not hasattr(original, ':staticman'):
def wrapped(*args, **kwargs):
return original(getattr(wrapped, ':staticman'), *args, **kwargs)
setattr(wrapped, ':staticman', StaticMan())
f = wrapped
else:
f = original #already wrapped
getattr(f, ':staticman')[name] = val
return f
return decorator
答案 14 :(得分:2)
惯用方式是使用类,它可以具有属性。如果您需要实例不分开,请使用单例。
有很多方法可以伪造或者将“静态”变量伪装成Python(到目前为止没有提到的是有一个可变的默认参数),但这不是 Pythonic,惯用这样做的方式。只需使用一个班级。
或者可能是生成器,如果您的使用模式适合。
答案 15 :(得分:2)
尝试了几种方法后,我最终使用了@warvariuc答案的改进版本:
import types
def func(_static=types.SimpleNamespace(counter=0)):
_static.counter += 1
print(_static.counter)
答案 16 :(得分:2)
您可以随时创建所谓的“函数对象”,并为其提供标准(非静态)成员变量,而不是创建具有静态局部变量的函数。
由于您给出了一个编写C ++的示例,我将首先解释C ++中的“函数对象”。 “函数对象”只是具有重载operator()
的任何类。该类的实例将表现得像函数一样。例如,即使int x = square(5);
是一个对象(具有重载square
)而且技术上不是“函数”,您也可以编写operator()
。您可以为函数对象提供可以为类对象提供的任何功能。
# C++ function object
class Foo_class {
private:
int counter;
public:
Foo_class() {
counter = 0;
}
void operator() () {
counter++;
printf("counter is %d\n", counter);
}
};
Foo_class foo;
在Python中,除了方法名为operator()
之外,我们还可以重载__call__
:
这是一个类定义:
class Foo_class:
def __init__(self): # __init__ is similair to a C++ class constructor
self.counter = 0
# self.counter is like a static member
# variable of a function named "foo"
def __call__(self): # overload operator()
self.counter += 1
print("counter is %d" % self.counter);
foo = Foo_class() # call the constructor
以下是正在使用的类的示例:
from foo import foo
for i in range(0, 5):
foo() # function call
打印到控制台的输出是:
counter is 1
counter is 2
counter is 3
counter is 4
counter is 5
如果您希望函数接受输入参数,也可以将它们添加到__call__
:
# FILE: foo.py - - - - - - - - - - - - - - - - - - - - - - - - -
class Foo_class:
def __init__(self):
self.counter = 0
def __call__(self, x, y, z): # overload operator()
self.counter += 1
print("counter is %d" % self.counter);
print("x, y, z, are %d, %d, %d" % (x, y, z));
foo = Foo_class() # call the constructor
# FILE: main.py - - - - - - - - - - - - - - - - - - - - - - - - - - - -
from foo import foo
for i in range(0, 5):
foo(7, 8, 9) # function call
# Console Output - - - - - - - - - - - - - - - - - - - - - - - - - -
counter is 1
x, y, z, are 7, 8, 9
counter is 2
x, y, z, are 7, 8, 9
counter is 3
x, y, z, are 7, 8, 9
counter is 4
x, y, z, are 7, 8, 9
counter is 5
x, y, z, are 7, 8, 9
答案 17 :(得分:1)
我个人更喜欢以下装饰者。每个人都有自己的。
def staticize(name, factory):
"""Makes a pseudo-static variable in calling function.
If name `name` exists in calling function, return it.
Otherwise, saves return value of `factory()` in
name `name` of calling function and return it.
:param name: name to use to store static object
in calling function
:type name: String
:param factory: used to initialize name `name`
in calling function
:type factory: function
:rtype: `type(factory())`
>>> def steveholt(z):
... a = staticize('a', list)
... a.append(z)
>>> steveholt.a
Traceback (most recent call last):
...
AttributeError: 'function' object has no attribute 'a'
>>> steveholt(1)
>>> steveholt.a
[1]
>>> steveholt('a')
>>> steveholt.a
[1, 'a']
>>> steveholt.a = []
>>> steveholt.a
[]
>>> steveholt('zzz')
>>> steveholt.a
['zzz']
"""
from inspect import stack
# get scope enclosing calling function
calling_fn_scope = stack()[2][0]
# get calling function
calling_fn_name = stack()[1][3]
calling_fn = calling_fn_scope.f_locals[calling_fn_name]
if not hasattr(calling_fn, name):
setattr(calling_fn, name, factory())
return getattr(calling_fn, name)
答案 18 :(得分:1)
以下装饰器可用于创建静态函数变量。它用自身的返回值替换声明的函数。这意味着被修饰的函数必须返回一个函数。
def static_inner_self(func):
return func()
然后在一个函数上使用装饰器,该函数返回另一个带有捕获变量的函数:
@static_inner_self
def foo():
counter = 0
def foo():
nonlocal counter
counter += 1
print(f"counter is {counter}")
return foo
nonlocal
是必需的,否则 Python 认为 counter
变量是局部变量而不是捕获的变量。由于变量赋值 counter += 1
,Python 的行为与此类似。函数中的任何赋值都会使 Python 认为该变量是局部变量。
如果你不是在内部函数中赋值给变量,那么你可以忽略nonlocal
语句,例如,在这个函数中我用来缩进一个字符串的行,Python可以推断出变量是 nonlocal
:
@static_inner_self
def indent_lines():
import re
re_start_line = re.compile(r'^', flags=re.MULTILINE)
def indent_lines(text, indent=2):
return re_start_line.sub(" "*indent, text)
return indent_lines
附言有一个已删除的答案提出了相同的建议。不知道作者为什么删了。 https://stackoverflow.com/a/23366737/195417
答案 19 :(得分:1)
全局声明提供了此功能。在下面的示例(使用“ f”的python 3.5或更高版本)中, counter 变量在函数外部定义。在功能中将其定义为全局表示表示该功能之外的“全局”版本应可用于该功能。因此,每次函数运行时,它都会修改函数外部的值,并将其保留在函数之外。
...
container: 'html',
...
答案 20 :(得分:1)
Soulution n + = 1
def foo():
foo.__dict__.setdefault('count', 0)
foo.count += 1
return foo.count
答案 21 :(得分:1)
这个答案表明,setdefault并不能真正满足OPs如何创建静态 本地 变量的问题。
def fn():
fn.counter = vars(fn).setdefault('counter',-1)
它的工作时间与fn一样长。以每个变量名称为前缀。如果你这样删除它们:
def fn():
counter = vars(fn).setdefault('counter',-1)
counter += 1
print (counter)
没有错误,但计数器总是0,这告诉我vars(fn)不是访问局部变量,而是一个全局的,可能是装饰器或属性存储。
如果这有效,那将是我的首选解决方案。但是,由于它没有,我倾向于使用完全封装的类定义来创建这样的静态变量。
恕我直言,这是最直截了当的。当然,这取决于你是否更熟悉功能与OOP编码风格。答案 22 :(得分:1)
当然这是一个老问题,但我想我可能会提供一些更新。
似乎性能论证已经过时了。 相同的测试套件似乎给出了siInt_try和isInt_re2的类似结果。 当然结果各不相同,但这是我的计算机上的一个会话,内核4.3.01与Xeon W3550的python 3.4.4。 我已经运行了几次,结果似乎相似。 我将全局正则表达式转换为函数static,但性能差异可以忽略不计。
isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632
由于性能问题已经解决,似乎try / catch会生成最具前瞻性和防止角落的代码,所以可能只是将其包装在函数中
答案 23 :(得分:1)
如果您不介意使用时髦的呼叫签名,那么可调用对象(如https://stackoverflow.com/a/279598/916373)的另一个(不推荐!)扭曲就是
meteor add twbs:bootstrap
class foo(object):
counter = 0;
@staticmethod
def __call__():
foo.counter += 1
print "counter is %i" % foo.counter
答案 24 :(得分:1)
Python方法中的静态变量
class Count:
def foo(self):
try:
self.foo.__func__.counter += 1
except AttributeError:
self.foo.__func__.counter = 1
print self.foo.__func__.counter
m = Count()
m.foo() # 1
m.foo() # 2
m.foo() # 3
答案 25 :(得分:0)
这个答案建立在@claudiu的回答之上。
我发现我的代码变得越来越不清楚 每当我打算访问静态变量时,都要预先添加函数名。
即,在我的功能代码中,我更愿意写:
print(statics.foo)
而不是
print(my_function_name.foo)
所以,我的解决方案是:
statics
属性
statics
作为my_function.statics
的别名from bunch import *
def static_vars(**kwargs):
def decorate(func):
statics = Bunch(**kwargs)
setattr(func, "statics", statics)
return func
return decorate
@static_vars(name = "Martin")
def my_function():
statics = my_function.statics
print("Hello, {0}".format(statics.name))
备注
我的方法使用名为Bunch
的类,这是一个支持的字典
属性风格的访问,一个JavaScript(参见original article关于它,大约2000年)
可以通过pip install bunch
它也可以像这样手写:
class Bunch(dict):
def __init__(self, **kw):
dict.__init__(self,kw)
self.__dict__ = self
答案 26 :(得分:0)
基于丹尼尔的答案(添加项):
class Foo(object):
counter = 0
def __call__(self, inc_value=0):
Foo.counter += inc_value
return Foo.counter
foo = Foo()
def use_foo(x,y):
if(x==5):
foo(2)
elif(y==7):
foo(3)
if(foo() == 10):
print("yello")
use_foo(5,1)
use_foo(5,1)
use_foo(1,7)
use_foo(1,7)
use_foo(1,1)
我想添加此部分的原因是,作为一个实际示例,静态变量不仅用于增加某个值,而且还检查静态var是否等于某个值。
静态变量仍然受到保护,并且仅在use_foo()函数的范围内使用
在此示例中,对foo()的调用功能完全相同(相对于对应的c ++而言):
stat_c +=9; // in c++
foo(9) #python equiv
if(stat_c==10){ //do something} // c++
if(foo() == 10): # python equiv
#add code here # python equiv
Output :
yello
yello
如果将Foo类严格地定义为单例类,那将是理想的。这会使它更具Pythonic性。
答案 27 :(得分:0)
但是,这是一篇安静的老文章,由于我有一个不同的惯用目标,因此我提出以下内容:
在一个函数中,我只想通过一个计算值初始化一次变量,这可能会花费一些成本。
因为我喜欢出色的写作,并且是一位古老的C风格程序员。 我试图定义类似宏的文字:
def Foo () :
StaticVar( Foo, ‘Var’, CalculateStatic())
StaticVar( Foo, ‘Step’, CalculateStep())
Foo.Var += Foo.Step
print(‘Value of Var : ‘, Foo.Var)
然后,我这样写了“ StaticVar”:
def StaticVar(Cls, Var, StaticVal) :
if not hasattr(Cls, Var) :
setattr(Cls, Var, StaticVal)
Python更好:
def StaticVars(Cls, **Vars) :
for Var, StaticVal in Vars.items() :
if not hasattr(Cls, Var) :
setattr(Cls, Var, StaticVal)
def Foo () :
StaticVars( Foo, Var = CalculateStatic(),Step= CalculateStep()))
Foo.Var += Foo. Step
print(‘Value of Var : ‘, Foo.Var)
这是一种不错的书写方式,但是我的目标(仅一次调用初始化函数)没有达到(只需在初始化函数中添加打印件)! 事实是,在函数调用中,参数值是在调用函数之前求值的。
def CalculateStatic() :
print("Costly Initialization")
return 0
为了达到我的目标,我宁愿写:
def Foo () :
if not hasattr(Foo, ‘Var’) :
setattr ( Foo, ‘Var’, CalculateStatic())
setattr ( Foo, ‘Step’, CalculateStep())
Foo.Var += Foo. Step
print(‘Value of Var : ‘, Foo.Var)
如果Python具有“ Marcro预处理”功能,那就更好了。
答案 28 :(得分:0)
我编写了一个使用静态变量的简单函数:
def Static():
### get the func object by which Static() is called.
from inspect import currentframe, getframeinfo
caller = currentframe().f_back
func_name = getframeinfo(caller)[2]
# print(func_name)
caller = caller.f_back
func = caller.f_locals.get(
func_name, caller.f_globals.get(
func_name
)
)
class StaticVars:
def has(self, varName):
return hasattr(self, varName)
def declare(self, varName, value):
if not self.has(varName):
setattr(self, varName, value)
if hasattr(func, "staticVars"):
return func.staticVars
else:
# add an attribute to func
func.staticVars = StaticVars()
return func.staticVars
使用方法:
def myfunc(arg):
if Static().has('test1'):
Static().test += 1
else:
Static().test = 1
print(Static().test)
# declare() only takes effect in the first time for each static variable.
Static().declare('test2', 1)
print(Static().test2)
Static().test2 += 1