这个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);
}
具体来说,如何在函数级别实现静态成员,而不是在类级别?将函数放入类中会改变什么吗?
当前回答
你可以创建一个所谓的“函数对象”,并给它一个标准的(非静态的)成员变量,而不是创建一个具有静态局部变量的函数。
既然你给出了一个c++编写的例子,我将首先解释什么是c++中的“函数对象”。“函数对象”就是任何带有重载操作符()的类。类实例的行为类似于函数。例如,你可以写int x = square(5);即使square是一个对象(带有重载操作符()),从技术上讲也不是一个“函数”。你可以给一个函数对象任何你可以给一个类对象的特性。
# 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
其他回答
当然,这是一个老问题,但我想我可以提供一些更新。
看来性能论点已经过时了。 对于siInt_try和isInt_re2,相同的测试套件似乎给出了类似的结果。 当然,结果会有所不同,但这是在我的计算机上使用python 3.4.4的一次会话,使用Xeon W3550的内核4.3.01。 我已经运行了几次,结果似乎相似。 我将全局正则表达式移动到函数静态,但性能差异可以忽略不计。
isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632
考虑到性能问题,try/catch似乎可以生成最适合未来和墙角情况的代码,所以可能只是将其包装在函数中
使用generator函数生成迭代器。
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的文档。
使用装饰器和闭包
下面的装饰器可用于创建静态函数变量。它将声明的函数替换为函数本身的返回值。这意味着被修饰的函数必须返回一个函数。
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的惯用方法。只需要使用一个类。
如果您的使用模式合适,也可以使用生成器。
有点相反,但这应该是有效的:
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
foo.counter = 0
如果你想让计数器初始化代码在顶部而不是底部,你可以创建一个装饰器:
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
它仍然需要你使用foo。前缀,不幸的是。
(图片来源:@ony)