这个C/ c++代码在Python中的等效代码是什么?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?
这个C/ c++代码在Python中的等效代码是什么?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?
当前回答
鉴于这个问题,我可以提出另一个可能更好用的替代方案,对方法和函数看起来都是一样的:
@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
其他回答
我写了一个简单的函数来使用静态变量:
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
您可以向函数添加属性,并将其用作静态变量。
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
无论如何,静态变量是相当罕见的,您应该为这个变量找到一个更好的位置,最有可能是在类中。
使用装饰器和闭包
下面的装饰器可用于创建静态函数变量。它将声明的函数替换为函数本身的返回值。这意味着被修饰的函数必须返回一个函数。
def static_inner_self(func):
return func()
然后在返回另一个带有捕获变量的函数的函数上使用decorator:
@static_inner_self
def foo():
counter = 0
def foo():
nonlocal counter
counter += 1
print(f"counter is {counter}")
return foo
nonlocal是必需的,否则Python认为计数器变量是一个局部变量而不是一个捕获变量。Python之所以如此,是因为变量赋值counter += 1。函数中的任何赋值都会使Python认为该变量是局部变量。
如果你没有在内部函数中为变量赋值,那么你可以忽略非局部语句,例如,在这个函数中,我用来缩进字符串的行,在这个函数中,Python可以推断出变量是非局部的:
@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
使用函数的属性作为静态变量有一些潜在的缺点:
每次要访问变量时,都必须写出函数的全名。 外部代码可以很容易地访问该变量并打乱值。
第二个问题的惯用python可能会用前导下划线来命名变量,以表明它不应该被访问,同时在事后保持它的可访问性。
使用闭包
另一种选择是使用词法闭包的模式,python 3中的nonlocal关键字支持这种模式。
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
遗憾的是,我不知道如何将这个解决方案封装到装饰器中。
使用内部状态参数
另一种选择可能是使用未记录的参数作为可变值容器。
def counter(*, _i=[0]):
_i[0] += 1
return _i[0]
这是可行的,因为默认参数是在定义函数时计算的,而不是在调用函数时计算的。
更清洁的方法可能是使用容器类型而不是列表,例如:
def counter(*, _i = Mutable(0)):
_i.value += 1
return _i.value
但我不知道内置类型,清楚地传达的目的。
在尝试了几种方法后,我最终使用了@warvariuc的答案的改进版本:
import types
def func(_static=types.SimpleNamespace(counter=0)):
_static.counter += 1
print(_static.counter)