【问题标题】:Strange behavior with a list as a default function argument [duplicate]将列表作为默认函数参数的奇怪行为[重复]
【发布时间】:2012-12-29 19:06:59
【问题描述】:

可能重复:
“Least Astonishment” in Python: The Mutable Default Argument
List extending strange behaviour
Pyramid traversal view lookup using method names

假设我有这个功能:

def a(b=[]):
    b += [1]
    print b

调用它会产生这个结果:

>>> a()
[1]
>>> a()
[1, 1]
>>> a()
[1, 1, 1]

当我将b += [1] 更改为b = b + [1] 时,函数的行为会发生变化:

>>> a()
[1]
>>> a()
[1]
>>> a()
[1]

b = b + [1]b += [1] 有何不同?为什么会这样?

【问题讨论】:

  • 您在函数范围内重新绑定b...使用b+= 不会这样做...
  • list += sequencelist = list + list不一样...

标签: python list default-arguments


【解决方案1】:

在 Python 中,不能保证 a += ba = a + b 做同样的事情。

对于列表,someList += otherList 在原地修改someList,基本上等同于someList.extend(otherList),然后将名称someList 重新绑定到同一个列表。另一方面,someList = someList + otherList 通过连接两个列表来构造一个 new 列表,并将名称 someList 绑定到该新列表。

这意味着,对于+=,名称最终指向它已经指向的同一个对象,而对于+,它指向一个新对象。由于函数默认值只计算一次(参见this much-cited question),这意味着+= 操作会堆积起来,因为它们都修改了相同的原始对象(默认参数)。

【讨论】:

  • > 然后将名称 someList 重新绑定到同一个列表。 extend 方法的列表。
  • @warwaruk:是的。如果分配给一个属性,您可以看到它(例如,创建一个其__setattr__ 打印出某些内容的对象,执行obj.someProp=[1],然后执行obj.someProp += [2] 并观察__setattr__ 被调用)。另请参阅here
  • 确实,+=+ 用于列表是两个独立的实现:List extending strange behaviour
  • 嗯,你是对的。在我弄清楚之前,我无法理解为什么 obj.a_list += another_list 等同于 obj.a_list = operator.iadd(obj.a_list, another_list) 而不是 obj.a_list.extend(another_list)
【解决方案2】:

b += [1] 更改函数默认值(导致最不惊讶的常见问题解答)。 b = b + [1] 采用默认参数b - 使用+ [1] 创建一个新列表,并将其绑定到b。一个改变列表 - 另一个创建一个新列表。

【讨论】:

    【解决方案3】:

    当你定义一个函数时

    >>> def a(b=[]):
        b += [1]
        return b
    

    它将所有默认参数保存在一个特殊的地方。它实际上可以通过以下方式访问:

    >>> a.func_defaults
    ([],)
    

    第一个默认值是list,其 ID:

    >>> id(a.func_defaults[0])
    15182184
    

    让我们尝试调用该函数:

    >>> print a()
    [1]
    >>> print a()
    [1, 1]
    

    并查看返回值的 ID:

    >>> print id(a())
    15182184
    >>> print id(a())
    15182184
    

    如您所见,它与第一个默认值的列表的ID相同。

    b+=... 就地修改了b 并且不创建新列表这一事实解释了函数的不同输出。 b 是保存在默认值元组中的列表。因此,您对列表的所有更改都保存在那里,并且函数的每次调用都使用不同的 b 值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-26
      • 1970-01-01
      相关资源
      最近更新 更多