当然,官方肯定建议是不要用可变参数作为函数的默认值,但是奈何肯定有很多犟牛,偏偏不信邪,然后就出现了很多奇奇怪怪的预期之外的现象。

不断变长的列表

最经典的案例当属下面这种不断变长的列表。

def test_default_value(item=["hello"]):
    print(item)
    item.append("world")


if __name__ == '__main__':
    test_default_value()  # ['hello']
    test_default_value()  # ['hello', 'world']
    test_default_value()  # ['hello', 'world', 'world']

期望

期望当然是希望 item 每次的默认值都是给定的 ["hello"] 。

现象

结果却是后面的运行中,item 使用了前面的运行结果,导致 item 越来越长。

原因

程序会调用定义函数时保存的默认参数,并在上一次的基础上进行操作叠加,即列表在 append 的时候会在 item 原来的基础上 append 追加值,所以导致列表越来越长。

永不变化的时间

import datetime
import time


def test_default_value(start_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")):
    print(start_time)


if __name__ == '__main__':
    test_default_value()  # 2025-09-30 17:00:35
    time.sleep(10)
    test_default_value()  # 2025-09-30 17:00:35
    time.sleep(10)
    test_default_value()  # 2025-09-30 17:00:35

期望

期望当然是希望 start_time 每次的默认值都是 datetime.datetime.now() 生成的最新时间。

现象

结果却是 start_time 始终保持第一次生成的时间不变,而不是每次生成当前的最新时间。

原因

默认参数只在函数定义时被计算一次,这个参数在函数被调用时不会被重新计算,所以导致后续运行的 start_time 始终没有再发生变化。

提前生成的时间

import datetime
import time


def test_default_value(start_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")):
    print(start_time)


if __name__ == '__main__':
    print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))  # 2025-09-30 17:02:51
    time.sleep(10)
    test_default_value()  # 2025-09-30 17:02:51
    time.sleep(10)
    test_default_value()  # 2025-09-30 17:02:51

期望

期望当然是希望 start_time 每次的默认值都是 datetime.datetime.now() 生成的最新时间。

现象

结果却是 start_time 始终保持程序开始运行的时间,也即这个默认值在函数真正运行前就已经生成好了,而不是在函数运行时才生成

原因

默认参数值在函数被定义时已经计算出来,而不是在函数运行时才开始计算,所以导致上面的时间不是函数运行的时间。

总结

当函数被定义时,默认参数的值只会被创建一次,并作为函数对象的属性保存,而不是每次调用函数时都重新创建(使用 id() 查看对象内存地址,可以验证默认参数是同一个对象)。

import datetime
import time


def test_default_value(start_time=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")):
    print(id(start_time))  # 即使多次调用函数,此默认参数对象的内存地址始终不变
    time.sleep(1)
    start_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")  # 重新赋值引用新的对象
    print(id(start_time))


if __name__ == '__main__':
    test_default_value()
    time.sleep(10)
    test_default_value()
    # 2973852936000
    # 2973852936560
    # 2973852936000
    # 2973852934000
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐