首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
第7章 编程术语
7.1 编程术语定义
7.1.1 作为语言的Python和作为解释器的Python
7.1.2 垃圾回收
7.1.3 字面量
7.1.4 关键字
7.1.5 对象、值、实例和身份
7.1.6 项
7.1.7 可变和不可变
7.1.8 索引、键和哈希值
7.1.9 容器、序列、映射和集合类型
7.1.10 特殊方法
7.1.11 模块和包
7.1.12 可调用对象和头等对象
7.2 经常被混淆的术语
7.2.1 语句和表达式
7.2.2 块、子句和主体
7.2.3 变量和特性
7.2.4 函数和方法
7.2.5 可迭代对象和迭代器
7.2.6 语法错误、运行时错误和语义错误
7.2.7 形参和实参
7.2.8 显式类型转换和隐式类型转换
7.2.9 属性和特性
7.2.10 字节码和机器码
7.2.11 脚本和程序,以及脚本语言和编程语言
7.2.12 库、框架、SDK、引擎、API
第8章 常见的Python陷阱
8.1 循环列表的同时不要增删其中的元素
8.2 复制可变值时务必使用copy.copy()和copy.deepcopy()
8.3 不要用可变值作为默认参数
8.4 不要通过字符串连接创建字符串
8.5 不要指望sort()按照字母顺序排序
8.6 不要假设浮点数是完全准确的
8.7 不要使用链式!=运算符
8.8 不要忘记在仅有一项的元组中添加逗号
第9章 Python的奇特难懂之处
9.1 为什么256 是256,而257 不是257
9.2 字符串驻留
9.3 假的Python 增量运算符和减量运算符
9.4 传递空列表给all()
9.5 布尔值是整数值
9.6 链式使用多种运算符
9.7 Python 的反重力特性
当前位置:
首页>>
技术小册>>
Python编程轻松进阶(三)
小册名称:Python编程轻松进阶(三)
### 8.3 不要用可变值作为默认参数 在Python编程中,函数定义时可以为参数指定默认值,这是Python提供的一种便利特性,允许在调用函数时省略某些参数,从而使用预定义的默认值。然而,当这些默认值被设置为可变类型(如列表、字典、集合等)时,就可能引发一系列意想不到的问题。这一章节将深入探讨为何应避免将可变值用作默认参数,并展示其潜在的问题及正确的做法。 #### 8.3.1 问题的根源 在Python中,函数定义时如果指定了默认参数,这些参数在函数定义时就被计算一次,并在后续的函数调用中保持不变(对于不可变类型如整数、浮点数、字符串和元组等),但对于可变类型(如列表、字典、集合等),情况就有所不同。可变类型的默认参数在函数定义时创建,并在后续函数调用中共享同一个对象引用。这意味着,如果函数内部修改了这些默认参数,那么这些修改将在后续的所有调用中持续存在,因为实际上修改的是同一个对象。 #### 8.3.2 示例说明 为了更清晰地说明这一点,让我们通过一个简单的例子来展示。 ```python def append_to_list(x, my_list=[]): my_list.append(x) return my_list # 第一次调用 print(append_to_list(1)) # 输出: [1] # 第二次调用,期望是 [2],但实际输出可能出人意料 print(append_to_list(2)) # 输出: [1, 2] # 第三次调用,列表继续累积 print(append_to_list(3)) # 输出: [1, 2, 3] ``` 在上述例子中,`append_to_list`函数旨在将传入的`x`添加到名为`my_list`的列表中,并返回这个列表。然而,由于`my_list`被设置为了一个默认参数且为可变类型(列表),因此每次调用`append_to_list`时,它都向同一个列表添加元素,而不是每次都从头开始。这通常不是我们想要的行为,因为它违反了函数调用的独立性原则。 #### 8.3.3 潜在问题 1. **难以追踪的bug**:如上例所示,如果函数的逻辑较为复杂,或者该函数被多个地方调用,那么使用可变默认参数可能导致难以追踪的bug,因为调用者可能无法预料到他们的调用会如何影响其他调用。 2. **代码可读性降低**:使用可变默认参数会使函数的行为变得不那么直观,增加代码的阅读和理解难度。 3. **测试困难**:在编写单元测试时,如果函数依赖于可变默认参数,那么测试可能会变得复杂,因为测试之间可能会相互影响。 #### 8.3.4 正确的做法 要避免这些问题,有几种方法可以采用: 1. **使用`None`作为默认值**:最常见的解决方法是将默认参数设置为`None`,然后在函数体内检查这个值,如果为`None`,则创建一个新的可变对象。 ```python def append_to_list(x, my_list=None): if my_list is None: my_list = [] my_list.append(x) return my_list print(append_to_list(1)) # 输出: [1] print(append_to_list(2)) # 输出: [2] print(append_to_list(3)) # 输出: [3] ``` 这种方法确保了每次调用`append_to_list`时,如果`my_list`没有被显式提供,就会创建一个新的空列表。 2. **使用文档字符串明确说明**:即使你决定不改变默认参数的类型,也应该在文档字符串中明确指出该函数的行为,特别是当默认参数是可变类型时。这有助于其他开发者(或未来的你)理解函数是如何工作的。 3. **考虑函数设计**:在设计函数时,考虑是否真的需要默认参数,或者是否有更好的方式来组织代码,比如使用类来封装状态和行为。 #### 8.3.5 进阶思考 虽然本章节主要讨论了可变默认参数的问题,但理解这一概念的背后原理对于编写高质量、可维护的Python代码至关重要。此外,对于其他可能引入状态或副作用的编程模式(如闭包中使用可变状态),也应保持警惕。 最后,值得注意的是,Python的设计哲学之一是“显式优于隐式”。通过将默认参数设置为`None`并在函数内部进行检查,我们实际上是在显式地告诉调用者:“这个函数需要一个列表,但如果你没有提供,我会为你创建一个新的。”这种做法提高了代码的清晰度和可预测性。 总之,避免将可变值作为默认参数是Python编程中的一个重要实践。通过采用上述建议,你可以编写出更加健壮、易于理解和维护的代码。
上一篇:
8.2 复制可变值时务必使用copy.copy()和copy.deepcopy()
下一篇:
8.4 不要通过字符串连接创建字符串
该分类下的相关小册推荐:
Python合辑5-格式化字符串
Python合辑3-字符串用法深度总结
Python机器学习基础教程(下)
Python合辑14-面向对象编程案例(下)
Python甚础Django与爬虫
Python高并发编程与实战
Python编程轻松进阶(一)
Python机器学习实战
Python合辑11-闭包函数
Python合辑13-面向对象编程案例(上)
Python高性能编程与实战
Python编程轻松进阶(五)