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
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))
...
class Bunch(dict):
def __init__(self, **kw):
dict.__init__(self,kw)
self.__dict__ = self
6
我个人更喜欢以下装饰器 . 给每个人自己 .
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)
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:
# 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
26 回答
使用函数的属性作为静态变量有一些潜在的缺点:
每次要访问变量时,都必须写出函数的全名 .
外部代码可以轻松访问变量并弄乱值 .
第二个问题的惯用python可能是用一个前导下划线来命名变量,表示它不是要访问的,同时在事后保持可访问性 .
另一种方法是使用词法闭包的模式,它在python 3中由
nonlocal
关键字支持 .可悲的是,我知道无法将此解决方案封装到装饰器中 .
使用生成器函数生成迭代器 .
然后就像使用它一样
如果你想要一个上限:
如果迭代器终止(如上例所示),你也可以直接循环它,就像
当然,在这些简单的情况下,最好使用xrange :)
这是yield statement的文档 .
洗脱n = 1
这是一个完全封装的版本,不需要外部初始化调用:
在Python中,函数是对象,我们可以通过特殊属性
__dict__
简单地向它们添加或修改成员变量 . 内置vars()
返回特殊属性__dict__
.编辑:注意,与替代
try:except AttributeError
答案不同,使用这种方法,变量将始终为初始化后的代码逻辑做好准备 . 我认为以下的try:except AttributeError
替代品将更少干燥和/或具有尴尬的流程:EDIT2:我只推荐上述方法,当从多个位置调用该函数时 . 如果只在一个地方调用该函数,最好使用
nonlocal
:很多人已经建议测试'hasattr',但有一个更简单的答案:
没有尝试/除,没有测试hasattr,只有getattr默认 .
与上面的vincent代码非常相似,它将用作函数装饰器,并且必须使用函数名作为前缀来访问静态变量 . 这段代码的优点(虽然不可否认任何人都可能很聪明地弄明白)是你可以有多个静态变量并以更传统的方式初始化它们 .
所有先前的解决方案都将计数器属性附加到函数,通常使用复杂的逻辑来处理初始化 . 这不适合新代码 .
在Python 3中,正确的方法是使用
nonlocal
语句:有关
nonlocal
语句的规范,请参见PEP 3104 .更具可读性,但更详细:
由this question提示,我是否可以提出另一个可能更好使用的替代方案,并且对于方法和函数看起来都相同:
如果您喜欢这种用法,请参考以下内容:
当然这是一个老问题,但我想我可能会提供一些更新 .
似乎性能论证已经过时了 . 相同的测试套件似乎给出了siInt_try和isInt_re2的类似结果 . 当然结果各不相同,但这是我的计算机上的一个会话,内核4.3.01上使用Xeon W3550的python 3.4.4 . 我已经运行了几次,结果似乎相似 . 我将全局正则表达式转换为静态函数,但性能差异可以忽略不计 .
由于性能问题已经解决,似乎try / catch会生成最具未来和防止角落的代码,所以可能只是将它包装在函数中
这个答案 Build 在@claudiu的回答之上 .
我发现当我打算访问一个静态变量时,我总是不得不在函数名前加上我的代码变得越来越清晰 .
也就是说,在我的功能代码中,我更愿意写:
代替
所以,我的解决方案是:
statics
属性在函数范围
statics
添加为my_function.statics
的别名备注
我的方法使用一个名为
Bunch
的类,这是一个支持属性样式访问的字典,一个JavaScript(请参阅original article关于它,大约2000)它可以通过
pip install bunch
安装它也可以像这样手写:
我个人更喜欢以下装饰器 . 给每个人自己 .
Python通常使用下划线来表示私有变量 . C在函数内声明静态变量的唯一原因是将它隐藏在函数外部,这不是真正的惯用Python .
Python没有静态变量,但您可以通过定义可调用的类对象然后将其用作函数来伪造它 . Also see this answer .
注意
__call__
使类(对象)的实例可以通过自己的名称调用 . 这就是为什么在上面调用foo()
会调用类'__call__
方法 . From the documentation:以丹尼尔的答案(补充)为基础:
我想添加这个部分的原因是,静态变量不仅用于递增某个值,还检查静态var是否等于某个值,作为现实生活中的例子 .
静态变量仍受保护,仅在函数use_foo()的范围内使用
在这个例子中,完全调用foo()函数(相对于相应的c等价物):
如果类Foo被限制性地定义为单例类,那将是理想的 . 这将使它更加pythonic .
全局声明提供此功能 . 在下面的示例中(使用"f"的python 3.5或更高版本),计数器变量在函数外部定义 . 在函数中将其定义为全局表示函数外部的"global"版本应该可用于该函数 . 因此,每次函数运行时,它都会修改函数外部的值,并将其保留在函数之外 .
有点逆转,但这应该工作:
如果您希望计数器初始化代码位于顶部而不是底部,则可以创建装饰器:
然后使用这样的代码:
不幸的是,它仍然需要你使用
foo.
前缀 .编辑(感谢ony):这看起来更好:
其他答案已经证明了你应该这样做的方式 . 这是你不应该这样做的方式:
默认值仅在首次计算函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来存储静态值 .
Python方法中的静态变量
您可以始终创建所谓的“函数对象”并为其提供标准(非静态)成员变量,而不是创建具有静态局部变量的函数 .
既然你给出了一个写C的例子,我将首先解释"function object"在C中的含义 . "function object"只是具有重载
operator()
的任何类 . 该类的实例将表现得像函数一样 . 例如,即使square
是一个对象(具有重载operator()
)而且技术上不是"function.",您也可以编写int x = square(5);
. 您可以为函数对象提供可以为类对象提供的任何功能 .在Python中,我们也可以重载
operator()
,除了该方法名为__call__
:这是一个类定义:
以下是正在使用的类的示例:
打印到控制台的输出是:
如果希望函数接受输入参数,也可以将它们添加到
__call__
:另一个(不推荐!)扭曲可调用对象,如https://stackoverflow.com/a/279598/916373,如果你不介意使用时髦的呼叫签名,那就是做
惯用的方法是使用一个可以具有属性的类 . 如果您需要实例不分开,请使用单例 .
有很多方法可以伪造或者将"static"变量伪装成Python(到目前为止没有提到的是有一个可变的默认参数),但这不是 Pythonic, idiomatic 方法 . 只需使用一堂课 .
如果您的使用模式适合,或者可能是发电机 .
您可以向函数添加属性,并将其用作静态变量 .
或者,如果您不想在函数外部设置变量,则可以使用
hasattr()
来避免AttributeError
异常:无论如何,静态变量相当罕见,你应该为这个变量找到一个更好的位置,很可能是在一个类中 .
人们还可以考虑:
推理:
多pythonic(
ask for forgiveness not permission
)使用异常(仅抛出一次)而不是
if
分支(想想StopIteration异常)这个答案表明setdefault并不能真正满足OPs如何创建静态 local 变量的问题 .
它的工作时间和fn一样长 . 以每个变量名称为前缀 . 如果你这样删除它们:
没有错误,但计数器总是0,这告诉我vars(fn)不是访问局部变量,而是一个全局,可能是装饰器或属性存储 .
如果这个工作,它将是我的首选解决方案 . 但是,由于它没有,我倾向于使用完全封装的类定义来创建这样的静态变量 .
恕我直言,这是最直截了当的 . 当然,这取决于您是否更熟悉功能与OOP编码样式 .
在尝试了几种方法后,我最终使用@ warvariuc的改进版本的答案: