Python:静态变量d

2024-04-26 17:24:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我想创建一个如下所示的decorator,但是我似乎想不出一个有效的实现。我开始觉得这不可能,但我想我会先问你们。

我意识到在Python中创建静态变量还有很多其他方法,但我觉得这些方法很难看。如果可能的话,我很想使用下面的语法。

@static(x=0)
def f():
    x += 1
    print x

f() #prints 1
f() #prints 2

我不在乎static的实现是长的还是黑的,只要它像上面那样工作。

我创建了这个版本,但它只允许使用<function>.<varname>语法,使用较长的函数名和变量名会很快变得很麻烦。

def static(**assignments):
    def decorate(func):
        for var, val in assignments.items():
            setattr(func, var, val)
        return func
    return decorate

我想到了很多事情,但都没能去上班:

  1. 将f(修饰函数)更改为可调用类,并以某种方式将静态变量透明地存储在self中。
  2. 在decorator中修改f()的全局变量,并以某种方式将“global x”语句插入到f的代码中
  3. 把f转换成一个生成器,在这里我们手工绑定变量,然后直接执行f的代码。

Tags: 方法函数returnvardef方式静态语法
3条回答

这里有一个非常简单的解决方案,它的工作原理与普通的python静态变量类似。

def static(**kwargs):
  def wrap(f):
    for key, value in kwargs.items():
      setattr(f, key, value)
    return f
  return wrap

示例用法:

@static(a=0)
def foo(x):
  foo.a += 1
  return x+foo.a

foo(1)  # => 2
foo(2)  # => 4
foo(14) # => 17

这更接近于python静态变量的常规处理方式

def foo(x):
  foo.a += 1
  return x+foo.a
foo.a = 10

当您的decorator获得函数对象f时,它已经被编译了——具体来说,它是在x是本地的(因为它被分配了+=分配)的知识下编译的,正常的优化(在2.*中,您可以以惊人的性能代价击败优化,使用exec '';在2.*中启动f,您无法击败优化)。本质上,要使用您渴望的语法,您必须重新编译f(通过恢复其源代码,如果您知道它们在运行时可用,或者更难的是,通过字节码破解)以某种方式修改源代码——一旦您决定这样做,最简单的方法可能是在f的整个主体中将x更改为f.x

就我个人而言,如果我发现自己在与语言(或其他技术)进行激烈的斗争,以至于我试图屈从于我的意愿去强加我的愿望,我承认我要么使用了错误的语言(或其他技术),如果这些愿望是绝对关键的,那么解决方法必须是改变技术;或者,如果这些欲望没有那么重要,就放弃它们。

不管怎样,我都放弃了试图扭曲语言,使之远离其明显的设计意图:即使我真的想出了一些老套、脆弱的废话,这无疑是不可挽回的。在本例中,Python的愿望非常明确:在函数中重新绑定的barenames是该函数的局部变量,除非显式指定为globals--period。所以,你试图使barenames(在函数中重新绑定)意味着与“locals”完全不同的东西正是这种斗争。

编辑:如果你愿意放弃对“静态”使用裸名的坚持,那么突然之间你不再与Python抗争,而是“随波逐流”(尽管有global[和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来修饰任何模块中的函数)。您可以有几个不同的实现,但任何好的实现的关键点是,“静态变量”将是限定的名称,不是裸名称--使其明确表示它们不是局部变量,并与语言的纹理一起玩,等等。(类似的内置容器和基于它们的限定名,应该在Python的设计中使用,而不是globalnonlocal设计问题,以指示其他类型的变量不是本地变量,因此不应该使用barenames。。。好吧,你可以在上面的同一行上实现一个globvar特殊容器,甚至不需要装饰,尽管我不太确定这对于nonlocal情况是完全可行的[也许带有一些装饰和最小数量的黑魔法…;=)]。

Edit注释指出,当您只修饰返回闭包的函数(而不是修饰闭包本身)时,给定的代码不起作用。没错:当然,您必须修饰使用static的特定函数(根据函数-static变量的定义,只能有一个),不是一个事实上不使用static的随机函数,而是恰好与使用的函数有某种词法上的联系。例如:

def f():
  @with_static(x=0)
  def g():
    static.x += 1
    print static.x
  return g

x = f()
x()
x()

当将decorator移到f而不是g时,这是可行的(而且不可能)。

如果实际的设计不是关于静态变量(仅在单个函数中可见和可用),而是一些在特定的pe中可用的混合变量烹饪函数包,需要非常精确地指定(毫无疑问,实现方式非常不同,具体取决于实际规格是什么),理想情况下,需要出现在一个新的、独立的SO问题中,因为这个问题(具体是关于静态的)和这个对这个特定的问题的答案问题,已经足够大了。

这是一个似乎很管用的装饰工。 注意,这需要在函数末尾返回locals(),因为无法从外部设置locals(我没有太多编程经验,所以如果有方法,我不知道)。

class Static(object):
    def __init__(self, **kwargs):
        self.kwargs = kwargs

    def __call__(self, f):
        def wrapped_f():
            try:
                new_kwargs = {}
                for key in self.kwargs:
                    i = getattr(f, key)
                    new_kwargs[key] = i
                self.kwargs = new_kwargs
            except:
                pass
            for key, value in f(**self.kwargs).items():
                setattr(f, key, value)
        return wrapped_f

@Static(x=0, y=5, z='...')
def f(x, y, z):
    x += 1
    y += 5
    print x, y, z
    return locals()

结果将是:

>>> f()
1 10 ...
>>> f()
2 15 ...
>>> f()
3 20 ...

编辑:

我在http://code.activestate.com/recipes/410698/找到了一些东西,并决定尝试将其添加到这个中。现在不用退货了。

再次编辑:更改为使速度加快几秒。 编辑3;改为函数而不是类

def static(**kwargs):
    def wrap_f(function):
        def probeFunc(frame, event, arg):
            if event == 'call':
                frame.f_locals.update(kwargs)
                frame.f_globals.update(kwargs)
            elif event == 'return':
                for key in kwargs:
                    kwargs[key] = frame.f_locals[key]
                sys.settrace(None)
            return probeFunc
        def traced():
            sys.settrace(probeFunc)
            function()
        return traced
    return wrap_f

测试:

@static(x=1)
def f():
    x += 1

global_x = 1
def test_non_static():
    global global_x
    global_x += 1


print 'Timeit static function: %s' % timeit.timeit(f)
print 'Timeit global variable: %s' % timeit.timeit(test_non_static)

输出:

Timeit static function: 5.10412869535
Timeit global variable: 0.242917510783

使用settrace会大大降低速度。

相关问题 更多 >