在调用期间,如何保持此模拟函数 - 静态变量的值?

时间:2012-02-29 17:41:23

标签: python

请参阅this

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>
     

默认值仅在首次计算函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来维护静态值。

问题>为什么counter可以在不同的呼叫期间保持其更新值? counter是否指向用于存储默认参数的临时列表的相同内存,以便它可以引用相同的内存地址并在调用期间保留更新的值?

4 个答案:

答案 0 :(得分:3)

作为默认参数创建的对象成为函数的一部分,并持续存在直到函数被销毁。

答案 1 :(得分:1)

默认参数在函数定义中计算,而不是在其调用。当创建foo函数对象时(记住,函数是python中的第一类公民),其参数(包括默认参数)都是绑定到函数对象的本地名称。首次遇到def:语句时会创建函数对象。

在这种情况下,counter被设置为foo()定义中的可变列表,因此在没有参数的情况下调用它会使原始可变列表在定义中实例化为名称counter。这使得对foo()的调用使用并修改原始列表。

答案 2 :(得分:1)

与所有函数参数一样,counter是声明函数时定义的本地名称。声明函数时,默认参数也会被评估一次。

如果在调用foo()时没有为counter传递任何值,则该默认值(在函数定义时提供的确切对象实例)将被赋予名称counter。由于这是一个可变对象(在本例中是一个列表),因此在函数完成后对它所做的任何更改都会保留。

该函数在其默认参数元组func_defaults中包含对列表的引用,以防止它被销毁。

答案 3 :(得分:1)

首先要做的事情:如果你用Python编码:忘记“内存地址” - 你永远不需要一个。是的,有对象,它们被放在内存中,如果你指的是同一个对象,它就在同一个“内存地址”中 - 但这并不重要 - 甚至可能有一个实现,其中对象没有有一个内存地址(例如,只是数据结构中的一个地方)。

然后,当Python遇到函数体时,如上所述,它会创建一个包含函数体内容的代码对象,并执行函数定义行 - 解析内联的任何表达式并设置它们的结果表达式作为该函数的默认参数。这些物体没有任何“暂时的”。计算表达式(在本例中为[0]),生成对象(在本例中为具有单个元素的Python列表),并将其分配给函数对象中的引用(函数中的位置“func_defaults” “属性 - 记住函数本身就是Python中的对象。”

每当调用该函数时,如果未将值传递给counter参数,则会将其分配给func_defaults属性中记录的对象 - 在本例中为Python列表。它与在函数解析时创建的Python列表相同。

发生的事情是Python列表本身是可变的:可以更改其内容,添加更多元素等等......但它们仍然是相同的列表。 上面的代码所做的就是精确地增加列表位置0中的元素。

您可以通过输入foo.func_defaults [0]随时在上面的示例中访问此列表 如果您想“重置”计数器,您可以执行:foo.func_defaults[0][0]=0,例如。

当然,它是如何在Python中重新实现的副作用,虽然一致,甚至是文档,但不应该在“真实代码”中使用。如果您需要“静态变量”,请使用类而不是上述函数:

class Foo(object):
  def __init__(self):
     self.counter = 0 
  def __call__(self):
    self.counter += 1 
    print("Counter is %i." % self.counter)

在控制台上:

>>> foo = Foo()
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>>