1

我有以下功能,我用 doctest 进行单元测试。

from collections import deque

def fill_q(histq=deque([])):
    """
    >>> fill_q()
    deque([1, 2, 3])
    >>> fill_q()
    deque([1, 2, 3])
    """
    if histq:
        assert(len(histq) == 0)
    histq.append(1)
    histq.append(2)
    histq.append(3)
    return histq

if __name__ == "__main__":
    import doctest
    doctest.testmod()

第一种情况通过,但对 fill_q 的第二次调用失败,但它是相同的代码:

**********************************************************************
File "trial.py", line 7, in __main__.fill_q
Failed example:
    fill_q()
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/doctest.py", line 1289, in __run
        compileflags, 1) in test.globs
      File "<doctest __main__.fill_q[1]>", line 1, in <module>
        fill_q()
      File "trial.py", line 11, in fill_q
        assert(len(histq) == 0)
    AssertionError
**********************************************************************
1 items had failures:
   1 of   2 in __main__.fill_q
***Test Failed*** 1 failures.

看起来 doctest 重新使用了histq第一次测试调用中的局部变量,为什么要这样做?这是非常愚蠢的行为(前提是我不是在这里疯狂地做某事)。

4

2 回答 2

1

问题不在于doctest,而在于您在 中使用的默认参数def fill_q(histq=deque([]))。它与此类似:

>>> from collections import deque
>>> 
>>> def fill_q(data=deque([])):
...     data.append(1)
...     return data
... 
>>> fill_q()
deque([1])
>>> fill_q()
deque([1, 1])
>>> fill_q()
deque([1, 1, 1])

当您将可变对象用作列表或字典之类的默认值时,就会发生这种看似奇怪的行为。它实际上使用的是同一个对象:

>>> id(fill_q())
4485636624
>>> id(fill_q())
4485636624
>>> id(fill_q())
4485636624

为什么?

def当且仅当它们所属的语句被执行时,默认参数值总是被评估[ ref ]。


如何避免这个错误:

None改为用作默认参数,或用于任意对象:

my_obj = object()
def sample_func(value=my_obj):
    if value is my_obj:
        value = expression
    # then modify value 

什么时候使用它?

  1. 全局名称的本地重新绑定:

    import math
    
    def fast_func(sin=math.sin, cos=math.cos):
    
  2. 可用于记忆(例如,使某些递归运行得更快)

于 2012-10-03T21:47:24.993 回答
1

你犯了一个非常常见的 Python 错误——如果你将一个对象设置为函数的默认构造函数,它不会在下一次调用该函数时重新初始化——并且对该对象的任何更改都将在函数调用中持续存在。

避免此问题的更好策略是将默认值设置为某个已知值,并检查它:

def fill_q(histq=None):
    if histq is None:
        histq = deque([])
    ...
于 2012-10-03T21:45:28.787 回答