什么是这个C/C++代码的惯用Python等价物?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
Run Code Online (Sandbox Code Playgroud)
具体来说,如何在功能级别实现静态成员,而不是类级别?将函数放入类中会改变什么吗?
Cla*_*diu 634
有点逆转,但这应该工作:
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
foo.counter = 0
Run Code Online (Sandbox Code Playgroud)
如果您希望计数器初始化代码位于顶部而不是底部,则可以创建装饰器:
def static_var(varname, value):
def decorate(func):
setattr(func, varname, value)
return func
return decorate
Run Code Online (Sandbox Code Playgroud)
然后使用这样的代码:
@static_var("counter", 0)
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
vin*_*ent 210
您可以向函数添加属性,并将其用作静态变量.
def myfunc():
myfunc.counter += 1
print myfunc.counter
# attribute must be initialized
myfunc.counter = 0
Run Code Online (Sandbox Code Playgroud)
或者,如果您不想在函数外部设置变量,则可以使用hasattr()以避免AttributeError异常:
def myfunc():
if not hasattr(myfunc, "counter"):
myfunc.counter = 0 # it doesn't exist yet, so initialize it
myfunc.counter += 1
Run Code Online (Sandbox Code Playgroud)
无论如何,静态变量相当罕见,你应该为这个变量找到一个更好的位置,很可能是在一个类中.
rav*_*yla 180
人们还可以考虑:
def foo():
try:
foo.counter += 1
except AttributeError:
foo.counter = 1
Run Code Online (Sandbox Code Playgroud)
推理:
ask for forgiveness not permission)if分支(想想StopIteration异常)小智 43
其他答案已经证明了你应该这样做的方式.这是你不应该这样做的方式:
>>> def foo(counter=[0]):
... counter[0] += 1
... print("Counter is %i." % counter[0]);
...
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
Run Code Online (Sandbox Code Playgroud)
默认值仅在首次计算函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来存储静态值.
Jon*_*han 33
很多人已经建议测试'hasattr',但有一个更简单的答案:
def func():
func.counter = getattr(func, 'counter', 0) + 1
Run Code Online (Sandbox Code Playgroud)
没有尝试/除,没有测试hasattr,只有getattr默认.
dan*_*els 25
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
Run Code Online (Sandbox Code Playgroud)
请注意,__call__使类(对象)的实例可以通过其自己的名称进行调用.这就是为什么调用foo()上面调用类的__call__方法.从文档:
通过
__call__()在类中定义方法,可以使任意类的实例可调用.
Ria*_*zvi 25
这是一个完全封装的版本,不需要外部初始化调用:
def fn():
fn.counter=vars(fn).setdefault('counter',-1)
fn.counter+=1
print (fn.counter)
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
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))
...
Run Code Online (Sandbox Code Playgroud)
gnu*_*nud 14
使用生成器函数生成迭代器.
def foo_gen():
n = 0
while True:
n+=1
yield n
Run Code Online (Sandbox Code Playgroud)
然后就像使用它一样
foo = foo_gen().next
for i in range(0,10):
print foo()
Run Code Online (Sandbox Code Playgroud)
如果你想要一个上限:
def foo_gen(limit=100000):
n = 0
while n < limit:
n+=1
yield n
Run Code Online (Sandbox Code Playgroud)
如果迭代器终止(如上例所示),你也可以直接循环它,比如
for i in foo_gen(20):
print i
Run Code Online (Sandbox Code Playgroud)
当然,在这些简单的情况下,最好使用xrange :)
以下是yield语句的文档.
其他解决方案通常使用复杂的逻辑来将计数器属性添加到函数,以处理初始化。这不适用于新代码。
在Python 3中,正确的方法是使用以下nonlocal语句:
counter = 0
def foo():
nonlocal counter
counter += 1
print(f'counter is {counter}')
Run Code Online (Sandbox Code Playgroud)
有关声明的说明,请参见PEP 3104nonlocal。
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)
Run Code Online (Sandbox Code Playgroud)
与上面的vincent代码非常相似,它将用作函数装饰器,并且必须使用函数名作为前缀来访问静态变量.这段代码的优点(尽管可以说任何人都可能足够聪明地弄明白)是你可以拥有多个静态变量并以更传统的方式初始化它们.
使用函数的属性作为静态变量有一些潜在的缺点:
第二个问题的惯用python可能是用一个前导下划线命名变量,表示它不是要访问的,同时在事后保持可访问性.
另一种方法是使用词法闭包的模式nonlocal,python 3中的关键字支持这种模式.
def make_counter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
counter = make_counter()
Run Code Online (Sandbox Code Playgroud)
可悲的是,我知道无法将此解决方案封装到装饰器中.
更具可读性,但更冗长(Python的Zen:显式优于隐式):
>>> def func(_static={'counter': 0}):
... _static['counter'] += 1
... print _static['counter']
...
>>> func()
1
>>> func()
2
>>>
Run Code Online (Sandbox Code Playgroud)
请参阅此处以了解其工作原理。
_counter = 0 def foo(): global _counter _counter += 1 print 'counter is', _counter
Python通常使用下划线来表示私有变量.C在函数内声明静态变量的唯一原因是将它隐藏在函数外部,这不是真正的惯用Python.
惯用的方法是使用可以具有属性的类。如果您需要实例不分离,请使用单例。
有多种方法可以将“静态”变量伪造或合并到 Python 中(到目前为止尚未提到的一种方法是使用可变的默认参数),但这不是Pythonic 的惯用方法。只需使用一个类即可。
或者可能是发电机,如果您的使用模式适合的话。
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
Run Code Online (Sandbox Code Playgroud)
在尝试了几种方法后,我最终使用了@warvariuc 答案的改进版本:
import types
def func(_static=types.SimpleNamespace(counter=0)):
_static.counter += 1
print(_static.counter)
Run Code Online (Sandbox Code Playgroud)
小智 5
全局声明提供了此功能。在下面的示例中(python 3.5 或更高版本使用“f”),计数器变量在函数外部定义。在函数中将其定义为全局意味着函数外部的“全局”版本应该可供该函数使用。因此,每次函数运行时,它都会修改函数外部的值,并将其保留在函数外部。
counter = 0
def foo():
global counter
counter += 1
print("counter is {}".format(counter))
foo() #output: "counter is 1"
foo() #output: "counter is 2"
foo() #output: "counter is 3"
Run Code Online (Sandbox Code Playgroud)
以下装饰器可用于创建静态函数变量。它用自身的返回值替换声明的函数。这意味着修饰函数必须返回一个函数。
def static_inner_self(func):
return func()
Run Code Online (Sandbox Code Playgroud)
然后在一个函数上使用装饰器,该函数返回另一个带有捕获变量的函数:
@static_inner_self
def foo():
counter = 0
def foo():
nonlocal counter
counter += 1
print(f"counter is {counter}")
return foo
Run Code Online (Sandbox Code Playgroud)
nonlocal是必需的,否则Python认为该counter变量是局部变量而不是捕获的变量。由于变量赋值,Python 的行为是这样的counter += 1。函数中的任何赋值都会使 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
Run Code Online (Sandbox Code Playgroud)
PS 有一个已删除的答案提出了相同的建议。不知道作者为什么删了。 /sf/answers/1635671621/
| 归档时间: |
|
| 查看次数: |
308966 次 |
| 最近记录: |