当你的装饰器获得函数对象时f
,它已经被编译了——具体来说,它是根据以下知识编译的:x
是本地的(因为它被分配了+=
分配),正常优化(在2.*
您可以通过开始以惊人的性能代价来击败优化f
with exec ''
; in 2.*
,你无法击败优化)。本质上,要使用您想要的语法,您必须重新编译f
(通过恢复其源代码,如果您知道它们将在运行时可用,或者更困难的是,通过字节码黑客)使用某种方式修改的源代码 - 一旦您决定这样做,最简单的方法可能是更改x
into f.x
整个身体f
.
就我个人而言,如果当我发现自己与语言(或其他技术)进行如此激烈的斗争,以至于我试图屈服于自己的意志来强加我的愿望时,我承认我要么使用了错误的语言(或其他技术) ,如果这些愿望绝对至关重要,那么解决方案必须是改变技术;或者,如果这些愿望不是那么重要,就放弃它们。
不管怎样,我放弃了试图扭曲语言,使其远离其明显的设计意图:即使我确实想出了一些古怪的、脆弱的拼凑,它无疑是无法维护的。在这种情况下,Python 的意图非常明确:在函数内重新绑定的裸名是该函数的局部变量,除非明确指定为全局变量——句号。因此,您尝试使裸名(在函数内重新绑定)意味着与“当地人”完全不同的东西正是这种斗争。
Edit:如果你愿意放弃坚持使用裸名对于你的“静态”,突然间你不再与 Python 作斗争,而是“顺应语言的本质”(尽管存在设计问题)global
[and nonlocal
],但是,这是一个单独的咆哮;-)。因此,例如:
class _StaticStuff(object):
_static_stack = []
def push(self, d):
self._static_stack.append(d)
def pop(self):
self._static_stack.pop()
def __getattr__(self, n):
return self._static_stack[-1][n]
def __setattr__(self, n, v):
self._static_stack[-1][n] = v
import __builtin__
__builtin__.static = _StaticStuff()
def with_static(**variables):
def dowrap(f):
def wrapper(*a, **k):
static.push(variables)
try: return f(*a, **k)
finally: static.pop()
return wrapper
return dowrap
@with_static(x=0)
def f():
static.x += 1
print static.x
f()
f()
这就像你想要的那样,打印 1 然后打印 2。(我正在使用__builtin__
使其使用起来最简单with_static
当然,可以装饰任何模块中的函数)。您可以有几种不同的实现,但任何一个的关键点good实现是“静态变量”将是合格的 names, notbarenames——明确表明它们不是局部变量,玩弄语言的纹理,等等。 (类似的内置容器,以及基于它们的限定名称,should在Python的设计中已经使用了,而不是global
and nonlocal
设计故障,以指示其他类型的变量不是本地变量,因此不应使用裸名...好吧,您可以自己实现一个globvar
与上述相同的特殊容器static
那些,甚至不需要装饰,尽管我不太确定这对于nonlocal
情况[也许with一些装饰和最少量的黑魔法...;=)])。
Edit:注释指出,当您仅装饰返回闭包的函数(而不是装饰闭包本身)时,给定的代码不起作用。没错:当然,你必须装饰使用的特定函数static
(根据函数的定义,只能有一个——static
变量!),而不是实际上不使用的随机函数static
而是恰好与以下词汇有某种词汇联系does。例如:
def f():
@with_static(x=0)
def g():
static.x += 1
print static.x
return g
x = f()
x()
x()
这有效,同时将装饰器移动到f
代替g
不会(也不可能)。
如果实际的需求不是关于静态变量(仅在单个函数中可见和可用),而是关于可在某个特定的函数包中使用的某种混合事物,则需要非常精确地指定(并且毫无疑问,实现方式非常不同,具体取决于实际规格是什么are)——理想情况下,这需要在一个新的、单独的 SO 问题中发生,因为这个(具体是关于static相反),而这个具体问题的答案已经足够大了。