的Python:静态变量装饰(Python: static variable decorator)

2019-08-31 13:53发布

我想创建一个装饰像下面,但我似乎不能认为工作的落实。 我开始认为这是不可能的,但想到我会问你们第一次。

我知道有各种其它方式使用Python创建静态变量,但我觉得丑陋的方式。 我真的希望,如果可以使用下面的语法。

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

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

我不在乎,如果实行static长或hackity,只要它的作品像上面。

我创造了这个版本,但只允许一个<function>.<varname>语法,而它与较长的函数和变量名繁琐很快。

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

各种事情,我想到了,但无法得到工作是:

  1. 改变F(装饰功能)插入一个可调用的类,并以某种方式存储静态VARS在self透明。
  2. 修改F()的全局装饰内部,并以某种方式将“全球X”语句到f的代码。
  3. 更改˚F到我们手工绑定变量,然后直接执行F公司代码发生器。

Answer 1:

这里,似乎工作一个装饰。 请注意,这需要在函数结束时,由于暂时无法从外部设定当地人返回当地人()(我没有太多的编程经验,所以如果有一种方法,我不知道)。

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减缓下来相当显着。



Answer 2:

通过你的装饰获取函数对象时f ,它已经被编译的-特别是,它被编译的知识x是本地的(因为它与分配的+=赋值),正常的优化(在2.*您可以打败了优化,以惊人的价格在性能,通过启动fexec '' ;在2.* ,你不能打败优化)。 从本质上讲,用你渴望的语法,你必须重新编译f以某种方式改性源(通过恢复其来源,如果你知道他们会在运行时使用,或者更难,通过字节码黑客) -一旦你”已经决定走那条路,最简单的方法可能是改变xfx整个身体f

就个人而言,如果当我发现自己的战斗这么辛苦对我试图屈从于我的意志强加给我的愿望的语言(或其他技术),我承认,我或者使用了错误的语言(或其他技术) ,如果这些欲望是绝对关键的,然后解决方案必须改变技术; 或者,如果这些欲望没有那么重要,放弃他们。

无论哪种方式,我放弃了试图歪曲的语言过于远离其明显用心的设计:即使我拿出一些哈克,脆弱的杂牌,那将无疑是不可维护的。 在这种情况下,Python的愿望意图是非常明确的:一个函数内得到重新绑定barenames是函数的当地人,除非明确地指定为全局 - 时期。 所以,你的企图使barenames(函数内得到重新绑定)意味着什么比“本地人”完全不同的是,正是这种斗争。

编辑 :如果你愿意放弃的坚持使用你的“静” barenames,然后突然你不对抗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装饰当然任何生活模块中的任何功能)。 你可以有多种不同的实现,但任何好的实现的关键点是“静态变量”将是合格的名称,而不是 barenames -摆明他们不是局部变量,用语言的粮食玩耍,等等。 (类似的内置容器中,并根据他们合格的名称, 应该已经在Python的设计中使用,而不是globalnonlocal的设计故障,指示其他类型的变量是不是本地的,因此不应该使用barenames被......啊,你可以实现自己一个globvar特殊的容器以上的同系static的,甚至无需装修,虽然我不那么肯定是在完全可行的nonlocal情况[也许一些装修的魔法最微小的量...; =)])。

编辑 :一评论指出,当你只是装饰,它返回一个封闭的函数给出的代码不工作(而不是装饰盖子本身)。 这是正确的:当然,你要装饰使用了特定的功能static (并且只能有一个,用的功能-定义static变量!),而不是一个随机函数,其实并没有使用static ,而是恰好是在与该的一个词汇的一些连接。 例如:

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

x = f()
x()
x()

这个工程,而将装饰以f而不是g无法(也不可能)。

如果实际的必要条件是不是静态变量(只在一个单一的功能可见性和可用性),但是,在整个的功能有一定独特的捆绑,需要非常精确地指定的可用一些混合的东西(毫无疑问非常不同的方式实现,这取决于什么实际的规格 ) -这需要一个新的独立的SO问题发生,因为这一个(这是专门关于静态代替),而这个回答这个具体问题,已经有很多足够大的理想。



Answer 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

这更加紧密地做蟒蛇静态变量的正常方式相匹配

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


Answer 4:

你可以做这样的事情(但我没有测试过这种广泛的;使用的CPython 2.6):

import types

def static(**dict):
    def doWrap(func):
        scope = func.func_globals
        scope.update(dict)
        return types.FunctionType(func.func_code, scope)
    return doWrap

# if foo() prints 43, then it's wrong
x = 42

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

foo() # => 1
foo() # => 2

它需要声明的变量为全局和阴影顶级的全局变量,但在其他方面应该工作。 不知道性能,虽然。



Answer 5:

这个怎么样,没有装饰?

class State(dict):
    """Object interface to dict."""
    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError, name

def f(d=State(x=0)):
    d.x += 1
    return d.x

这里是它在行动:

>>> f()
1
>>> f()
2
>>> f()
3


Answer 6:

这里的东西,可能是更清晰。 它不涉及任何装饰或黑客攻击。

class F( object ):
    def __init__( self ):
        self.x= 0
    def __call__( self ):
        self.x += 1
        print self.x

f= F()

现在,你有你的功能f用一个静态变量。

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


Answer 7:

当你需要保存的函数调用之间的状态下,你几乎总是最好使用生成器/协同或对象。 既然你想用“裸”变量名,那么你会希望协程版本。

# the coroutine holds the state and yields rather than returns values
def totalgen(x=0, y=0, z=0):
    while True:
       a, b, c = (yield x, y, z)
       x += a
       y += b
       z += c

# the function provides the expected interface to callers
def runningtotal(a, b, c, totalgen=totalgen()):
    try:
        return totalgen.send((a, b, c))    # conveniently gives TypeError 1st time
    except TypeError:
        totalgen.next()                    # initialize, discard results
        return totalgen.send((a, b, c))

其结果是,积聚传递给它的三个值的总和,就好像它有静态变量的函数,但蓄电池在本质上是一个无限发电机普通的老的局部变量。



Answer 8:

有轻微的调整另一个anwser :

def static(**kwargs):
    def decorator(func):
        return type(func)(func.func_code, dict(func.func_globals, **kwargs))
    return decorator

message = "goodbye, world!"
@static(message="hello, world!")
def hello(): print message

hello()

我觉得恶心覆盖一个内置的名字与函数参数的名字,所以我改变了**dict到更规范**kwargs 。 我还保存了几行,IMO通过构建一个新的字典使代码更清洁dict(the_original, **the_updates) 最后我通过访问通过函数构造保存几行type(func)而不是进口---类型和类对象是工厂方法,所以使用它们!

我也删除一个global的声明。 这工作只要你不重新绑定变量,即删除global实际上就是把指针(而不是对象)为只读。 如果你使用这种方式,也许let比一个更好的名字static对于这样的绑定介绍。



文章来源: Python: static variable decorator