【问题标题】:Why doesn't Python have static variables?为什么 Python 没有静态变量?
【发布时间】:2010-10-10 05:25:56
【问题描述】:

有一个questions asking how to simulate static variables in python

此外,在网络上可以找到许多不同的解决方案来创建静态变量。 (虽然我还没有看到我喜欢的。)

为什么 Python 不支持方法中的静态变量?这被认为是不符合 Python 的还是与 Python 的语法有关?

编辑:

我专门询问了设计决策的为什么,我没有提供任何代码示例,因为我想避免解释来模拟静态变量。

【问题讨论】:

标签: python


【解决方案1】:

在 Python 3 中,我会使用闭包:

def makefoo():
    x = 0
    def foo():
        nonlocal x
        x += 1
        return x
    return foo

foo = makefoo()

print(foo())
print(foo())

【讨论】:

  • 您好,我仍在尝试弄清楚这是如何工作的。你能再解释一下吗?提前致谢。 :)
【解决方案2】:

这个省略背后的想法是静态变量只在两种情况下有用:当你真的应该使用类时,以及当你真的应该使用生成器时。

如果你想将状态信息附加到一个函数,你需要一个类。也许是一个简单的类,但仍然是一个类:

def foo(bar):
    static my_bar # doesn't work

    if not my_bar:
        my_bar = bar

    do_stuff(my_bar)

foo(bar)
foo()

# -- becomes ->

class Foo(object):
    def __init__(self, bar):
        self.bar = bar

    def __call__(self):
        do_stuff(self.bar)

foo = Foo(bar)
foo()
foo()

如果您希望函数的行为在每次调用时都发生变化,那么您需要一个生成器:

def foo(bar):
    static my_bar # doesn't work

    if not my_bar:
        my_bar = bar

    my_bar = my_bar * 3 % 5

    return my_bar

foo(bar)
foo()

# -- becomes ->

def foogen(bar):
    my_bar = bar

    while True:
        my_bar = my_bar * 3 % 5
        yield my_bar

foo = foogen(bar)
foo.next()
foo.next()

当然,静态变量对于你不想为小任务处理大结构的麻烦的快速而肮脏的脚本很有用。但是在那里,你真的不需要比global 更多的东西——它可能看起来很笨拙,但对于小型的一次性脚本来说没关系:

def foo():
    global bar
    do_stuff(bar)

foo()
foo()

【讨论】:

  • 好吧,你确实可能需要上课。除了丑陋的 Borg 模式,没有真正的方法可以确保这个类是单例。其实包的导入是一件很痛苦的事情。
  • 类示例看起来不像是 Python。我认为您的意思是:“私人酒吧”=>“酒吧=无”,在初始化时您将拥有“self.my_bar = bar”。
  • @Heikki Toivonen:同意,在第一个示例中,“my_bar”也不必是类变量,实例变量也可以。
  • @Paul:只使用类级别的变量和@classmethod 装饰器,你的类也是一个单例对象。
  • +1 表示概念,python 代码需要清理,正如上面的 cmets 所说:)
【解决方案3】:

来自您的一个 cmets:“我想使用它们来缓存从磁盘加载的内容。如果我可以将它们分配给函数,我认为它会减少实例的混乱”

然后使用缓存类,作为其他类的类或实例属性。这样,您可以使用完整的类功能集,而不会弄乱其他东西。此外,您还可以获得一个可重复使用的工具。

这表明在 SO 上,陈述问题而不是要求特定的低级解决方案(例如缺少语言功能)总是值得的。这样一来,就不必无休止地争论如何模拟“静态”(在我看来,这是一种古老语言中已弃用的功能),有人可以更快地为您的问题提供一个很好的答案。

【讨论】:

  • 我知道如何模拟它们。问题是关于决定的。
【解决方案4】:

一个不明智的选择:

您还可以使用函数默认值的定义时间评估的副作用:

def func(initial=0, my_static=[])
  if not my_static:
    my_static.append(initial)

   my_static[0] += 1
  return my_static[0]

print func(0), func(0), func(0)

它的真的丑陋且容易被颠覆,但有效。使用global 会比这更干净,imo。

【讨论】:

    【解决方案5】:

    类的另一种选择是函数属性:

    def foo(arg):
        if not hasattr(foo, 'cache'):
            foo.cache = get_data_dict()
        return foo.cache[arg]
    

    虽然类可能更干净,但在我看来,这种技术很有用而且更好,然后是全局。

    【讨论】:

    • 我不喜欢的是需要一遍又一遍地写方法名。如果我更改名称,我将不得不重写一半的代码。
    • @gs:是的,这是一个缺点。我一直在寻找某种方法来引用当前函数,而不是使用名称(类似于 self),但我认为没有这样的方法。你可以在最顶部做一个“this = foo”,然后在任何地方引用“this”,这样重命名就很容易维护。
    • 这可以按原样工作,但是当有人传递 foo 并收到 NameError: global name 'foo' is not defined
    • @jleedev: 使用的全局变量(包含名称“foo”)是从函数创建时开始的模块全局变量,而不是从哪里调用它(这意味着动态范围,而不是词法)。传递它不会有任何区别。
    【解决方案6】:

    对于缓存或memoization 目的,装饰器可以用作优雅且通用的解决方案。

    【讨论】:

      【解决方案7】:

      答案与为什么没有人使用静态方法(即使它们存在)几乎相同。你有一个模块级命名空间,其用途与类的用途大致相同。

      【讨论】:

        【解决方案8】:

        这是一种设计选择。

        我假设 Guido 认为你并不经常需要它们,而且你从来没有真正需要它们:你总是可以只使用一个全局变量并告诉每个人不要再用油腻的爪子了'你的变量;-)

        【讨论】:

        • 呃,或者你可以像往常一样传递对象。
        • 假设您想在快速排序中使用自己的随机生成器。调用者应该传递随机性吗?不。是否应该在(否则独立的)调用之间进行更新?是的。有时您不想传递对象...
        【解决方案9】:

        我认为局部静态变量的大多数用途是模拟生成器,即有一些函数执行某个过程的某些迭代,返回结果,但为后续调用保留状态。 Python 使用yield 命令非常优雅地处理了这个问题,因此似乎不需要静态变量。

        【讨论】:

        • 我想用它们来缓存从磁盘加载的东西。如果我可以将它们分配给函数,我认为它会减少实例的混乱。
        猜你喜欢
        • 2012-08-29
        • 2016-01-15
        • 1970-01-01
        • 2011-12-08
        • 2010-09-25
        • 1970-01-01
        • 1970-01-01
        • 2011-09-13
        相关资源
        最近更新 更多