【问题标题】:Static variable in Python?Python中的静态变量?
【发布时间】:2014-12-25 04:26:35
【问题描述】:

在 C++ 中,我们有 static 关键字,在循环中是这样的:

for(int x=0; x<10; x++)
    {
      for(int y=0; y<10; y++)
      {
        static int number_of_times = 0;
        number_of_times++;
      }
    }

这里的静态使number_of_times 初始化一次。如何在 python 3.x 中做同样的事情?

编辑:由于大多数人感到困惑,我想指出我提供的代码只是 C++ 中静态用法的示例。我真正的问题是我只想在函数中初始化一个时间变量,因为我不希望它是全局的(废话!)或默认参数..

【问题讨论】:

  • 这不是声明局部变量static 所做的唯一事情。请告诉我们您想在 Python 中对变量做什么,以及您实际尝试解决的问题 (read about the XY problem)。
  • @kuskmen 然后在您的问题中显示 that,而不是不相关的循环示例。
  • @kuskmen:默认参数有什么问题?
  • 你考虑过类和对象吗?
  • @kuskmen:这里的虚拟示例pastie.org/9685335。如果这对您没有意义,在您的真实用例中发布您的问题。

标签: python python-3.x


【解决方案1】:

假设你想要的是“一个在第一次函数调用时只初始化一次的变量”,那么 Python 语法中没有这样的东西。但是有一些方法可以获得类似的结果:

1 - 使用全局。请注意,在 Python 中,“全局”实际上意味着“模块全局”,而不是“进程全局”:

_number_of_times = 0

def yourfunc(x, y):
    global _number_of_times
    for i in range(x):
        for j in range(y):
            _number_of_times += 1

2 - 将代码包装在一个类中并使用类属性(即:所有实例共享的属性)。 :

class Foo(object):
    _number_of_times = 0

    @classmethod
    def yourfunc(cls, x, y):
        for i in range(x):
            for j in range(y):
                cls._number_of_times += 1

请注意,我使用了classmethod,因为这段代码 sn-p 不需要实例中的任何内容

3 - 将代码包装在一个类中,使用实例属性并为方法提供快捷方式:

class Foo(object):
    def __init__(self):
         self._number_of_times = 0

    def yourfunc(self, x, y):
        for i in range(x):
            for j in range(y):
                self._number_of_times += 1

yourfunc = Foo().yourfunc

4 - 编写一个可调用的类并提供一个快捷方式:

class Foo(object):
    def __init__(self):
         self._number_of_times = 0

    def __call__(self, x, y):
        for i in range(x):
            for j in range(y):
                self._number_of_times += 1


yourfunc = Foo()

4 bis - 使用类属性和元类

class Callable(type):
    def __call__(self, *args, **kw):
        return self._call(*args, **kw)

class yourfunc(object):
    __metaclass__ = Callable

    _numer_of_times = 0

    @classmethod
    def _call(cls, x, y):
        for i in range(x):
            for j in range(y):                 
                cls._number_of_time += 1

5 - 对函数的默认参数进行“创造性”使用,在模块导入时仅实例化一次:

def yourfunc(x, y, _hack=[0]):
    for i in range(x):
        for j in range(y):
            _hack[0] += 1

还有一些其他可能的解决方案/技巧,但我认为您现在了解全局了。

编辑:鉴于操作的说明,即“假设您有一个带有默认参数的递归函数,但如果有人实际上试图为您的函数再提供一个参数,那可能是灾难性的”,看起来 OP 真正想要的是类似:

# private recursive function using a default param the caller shouldn't set
def _walk(tree, callback, level=0):
    callback(tree, level)
    for child in tree.children:
        _walk(child, callback, level+1):

# public wrapper without the default param
def walk(tree, callback):
    _walk(tree, callback)

顺便说一句,这证明我们确实遇到了另一个 XY 问题...

【讨论】:

  • 我不明白为什么项目“4 bis”被这样标记。为什么不叫它 5 而叫当前的 5 数字 6?
  • @StevenRumbalski 因为这是一个编辑,我不想弄乱已经存在的数字;)
【解决方案2】:

您可以使用nonlocal 创建一个闭包以使它们可编辑(仅限python 3.x)。下面是一个计算列表长度的递归函数示例。

def recursive_len(l):
    res = 0
    def inner(l2):
        nonlocal res
        if l2:
            res += 1
            inner(l2[1:])
    inner(l)
    return res

或者,您可以为函数本身分配一个属性。使用here的技巧:

def fn(self):
    self.number_of_times += 1
fn.func_defaults = (fn,)
fn.number_of_times = 0

fn()
fn()
fn()
print (fn.number_of_times)

【讨论】:

  • “函数本身的属性”是非常脆弱的,因为它依赖于名称 ('fn') 和函数对象之间的全局绑定来保持稳定。
  • 为什么不在函数定义后直接赋值函数属性呢? fn.number_of_times = 0.
  • @StevenRumbalski 我已经编辑了,谢谢。我想是有人先建议的,但他们已经删除了。
  • @parchment:你为什么还有self 参数?如果您需要self,您也可以使用具有__call__ 方法的类属性的类。如果你摆脱了self,你的函数会按照 bruno desthuilliers 的说法被破坏:from somemodule import fn as somethingelse
  • @StevenRumbalski 它来自 SO 答案 herefn.func_defaults 行将参数 self 更改为函数本身的默认值。
【解决方案3】:

Python 没有静态变量by design。对于您的示例,通常在循环块等中使用,您只需在外部范围内使用变量;如果这让它的寿命太长,那么可能是时候考虑将该功能分解为更小的功能了。

对于在函数调用之间继续存在的变量,这只是重新实现了对象和该对象上的方法的基本思想,因此您应该改用其中一个。

【讨论】:

    【解决方案4】:

    在 python 中另一种基于函数的方法是:

    def f(arg, static_var=[0]):
        static_var[0] += arg
    

    由于static_var 对象在函数定义中被初始化,然后被所有调用重用,它就像一个静态变量。请注意,您不能只使用 int,因为它们是不可变的。

    >>> def f(arg, static_var=[0]):
    ...         static_var[0] += arg
    ...         print(static_var[0])
    ... 
    >>> f(1)
    1
    >>> f(2)
    3
    >>> f(3)
    6
    

    【讨论】:

    • 我实际上正试图做与此完全相反的事情:) 我想删除默认参数初始化 static_var = [](例如)ONCE 然后更改它但是我喜欢在 C++ 中这可以是使用 static 关键字完成,但显然此功能未在 python 中实现
    【解决方案5】:

    你也可以使用全局关键字:

    def main(args):
        for i in xrange(10):
            print i
            global tmp 
            tmp = i
    

    但要小心……在大多数情况下,它会增加比它解决的问题更多的问题。

    【讨论】:

      【解决方案6】:

      使用defaultdict:

      from collections import defaultdict
      
      static = defaultdict(lambda: 0)
      
      def myfunc():
          for x in range(10):
              for y in range(10):
                  static['number_of_times'] += 1
      

      【讨论】:

        猜你喜欢
        • 2021-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        • 2014-02-12
        • 2011-04-11
        • 1970-01-01
        相关资源
        最近更新 更多