当前位置: 首页 > news >正文

函数默认值的陷阱:用None和Docstring来描述可变默认值的参数 (Effective Python 第24条)

在Python中,函数的默认参数值只在定义函数时计算一次。这一特性可能会导致一些意想不到的问题,尤其是在默认参数是可变对象(如字典、列表或日期时间对象)的情况下。本文将探讨这一问题,并提供解决方案,包括使用Nonedocstring来描述默认值会变化的参数,以及如何与类型注解结合使用。


问题:函数默认值的“坑”

假设我们有一个函数,其默认参数是一个字典或列表:

def add_item(items=[]):items.append("default item")return items

当我们调用这个函数时,第一次调用会返回['default item'],但第二次调用会返回['default item', 'default item'],第三次调用会返回['default item', 'default item', 'default item'],依此类推。这是因为默认参数[]在函数定义时被计算了一次,并且后续的调用都会共享同一个列表对象。

类似地,如果我们使用一个字典作为默认参数:

def update_config(config={}):config["key"] = "value"return config

每次调用这个函数时,config字典都会被修改,导致所有调用共享同一个字典对象。

此外,如果我们希望默认参数是动态变化的值(如当前日期时间),也会遇到类似的问题:

import datetimedef log_time(timestamp=datetime.datetime.now()):print(f"Logged at {timestamp}")

无论我们调用这个函数多少次,timestamp的值都会是函数定义时的日期时间,而不是每次调用时的当前时间。


解决方法:将默认值设为None,并在函数体中初始化

为了避免上述问题,我们可以将默认参数设为None,并在函数体中进行初始化。这样,每次调用函数时都会创建一个新的对象。

示例1:避免共享可变对象

def add_item(items=None):if items is None:items = []items.append("default item")return items

在这个示例中,items的默认值是None。每次调用函数时,如果itemsNone,我们都会创建一个新的空列表。这样,每次调用函数时都不会共享同一个列表对象。

示例2:动态默认值

import datetimedef log_time(timestamp=None):if timestamp is None:timestamp = datetime.datetime.now()print(f"Logged at {timestamp}")

在这个示例中,timestamp的默认值是None。每次调用函数时,如果timestampNone,我们都会获取当前的日期时间。这样,每次调用函数时都会使用最新的时间。


docstring中描述默认行为

除了将默认参数设为None,我们还应该在docstring中明确描述函数的默认行为。这样,其他开发者或用户可以清楚地了解函数的默认参数是如何工作的。

示例:在docstring中描述默认行为

def add_item(items=None):"""向列表中添加一个默认项。Args:items (list, optional): 要操作的列表。默认为None,此时会创建一个空列表。Returns:list: 添加默认项后的列表。"""if items is None:items = []items.append("default item")return items

在这个docstring中,我们明确说明了items的默认行为:如果itemsNone,则会创建一个空列表。


结合类型注解使用

Python 3.10及以上版本支持|符号来表示联合类型注解。我们可以将None和具体的类型结合起来,以进一步明确参数的类型。

示例:结合类型注解使用

def add_item(items: list | None = None):"""向列表中添加一个默认项。Args:items (list | None): 要操作的列表。默认为None,此时会创建一个空列表。Returns:list: 添加默认项后的列表。"""if items is None:items = []items.append("default item")return items

在这个示例中,我们使用类型注解list | None来明确items的类型:它可以是一个列表,也可以是None


总结

函数默认参数值只在定义函数时计算一次,这可能会导致一些意想不到的问题,尤其是在默认参数是可变对象或动态值的情况下。为了避免这些问题,我们可以采取以下措施:

  1. 将默认参数设为None ,并在函数体中进行初始化。
  2. docstring中明确描述默认行为,以便其他开发者或用户清楚函数的行为。
  3. 结合类型注解,以进一步明确参数的类型。

通过这些方法,我们可以写出更健壮、更易维护的代码,避免默认参数带来的潜在问题。

http://www.dtcms.com/a/351531.html

相关文章:

  • Qt 窗口 - 1
  • [Maven 基础课程]IDEA 配置 Maven
  • 基于 W55MH32Q-EVB 实现 FatFs 文件系统+FTP 服务器
  • C++ SNIFE
  • 数据结构——链表(2)——双向链表
  • 【图像处理 - 基础知识】ISP(Image Signal Processor)处理
  • 深度学习之卷积神经网络
  • 组件版本升级 - MySQL_8.4对比MySQL_8.0
  • 用 mem0 构建一个简单的 AI Healthcare 助手
  • 基于Vue通用组件定制化的场景解决
  • UNet改进(35):基于WGAM模块的PyTorch实战
  • Qt在Linux下编译发布 -- linuxdeployqt的使用
  • 第十九节:阴影进阶 - 软阴影与性能平衡技术
  • FileMenu Tools for Win:突破 Windows 文件管理困境的利器
  • Git:基本使用
  • 数字化转型三阶段:从信息化、数字化到数智化的战略进化
  • Leetcode+Java+动态规划II
  • 知行——同为科技24周年庆典
  • Thingsboard 租户管理员权限,增加租户普通用户权限
  • Go errgroup:高效并发控制与错误处理
  • WPF基于LiveCharts2图形库,实现:折线图,柱状图,饼状图
  • 03. 协程入门_Android异步处理机制
  • 系统架构设计师备考第7天——网络协议中间件软件构件
  • WebSocket简单了解
  • 线性代数之深入理解旋转矩阵
  • lesson46-2:Linux 高级指令全解析:从文件操作到系统管理
  • mybatisplus 配置二级缓存
  • 【系统编程】线程简介
  • 【人工智能】2025年AI代理开源革命:社区驱动的智能体生态重塑未来
  • Linux--seLinux的概述