学过一遍 Python,写代码却总踩'奇怪'的坑?变量莫名被修改、函数作用域搞不清、装饰器似懂非懂……其实是你忽略了那些'小而关键'的细节。本文专为学过一遍 Python 的开发者梳理 Python 中值得注意的细节,从对象引用、可变性、作用域到导入机制、装饰器等,帮你打通认知盲区,写出更健壮、地道的 Python 代码。
- 在 Python 中,我们实际上并未为变量分配值。相反,Python 将对象(值)的引用提供给变量。
- True 和 False 在数值上下文中分别等价于 1 和 0,但它们是 bool 类型,不是整数。
- Python 中的每个值都有一个数据类型。由于在 Python 中一切皆对象,因此数据类型实际上是类,而变量是这些类的实例(对象)。
- 字符串前加 r 或 R 表示原始字面量,不转义。
- Python 中常见的不可变类型有:数字、bool、str、tuple。特点:对不可变对象进行'修改'操作(如拼接字符串、给元组添加元素)实际上会创建一个新对象。
- Python 中常见的可变类型有:list、dict、set,自定义类的实例。特点:可以在原地修改对象内容,不会创建新对象(例如
list.append())。其中自定义类的实例默认可变,除非设计为不可变类型。 - Python 中常见的容器和序列都是可迭代的,而像 None、数字、布尔值、函数等则不可迭代。
- Python 中的区间都是左闭右开,除了 random 模块中的
randint()是全闭区间。 - Python 字符串是 Unicode 字符的序列,故一个非 ASCII 字符的长度也是 1。
- 在函数内部引用一个变量时,Python 按 LEGB 规则依次搜索:
- L(Local):局部作用域(当前函数)
- E(Enclosing):嵌套的外部函数作用域(闭包)
- G(Global):全局作用域(模块级别)
- B(Built-in):内置作用域(如 len, print)
若存在嵌套函数,内层函数可以读取外层函数的变量(因为 E 存在)。若仅修改可变对象的内容(如
list.append、dict.update),不需要 nonlocal;若对外部变量进行赋值(即重新绑定),则必须使用 nonlocal(在嵌套函数中)。
- 在函数外部定义的变量属于全局变量,函数内部未用 global 关键字声明的变量属于局部变量,global 只能在函数内部使用。
del my_list[i]或my_list.remove(指定元素)都会删除一个元素,并使后续元素前移,列表长度 -1,其中remove()只删除匹配到的第一个元素。del my_list是删除列表对象,无法再次访问,而my_list.clear()只是清空列表元素,对象还在。- 在 list 操作中,假设 x 是可迭代对象,则
append(x)是将 x 作为一个整体添加到原列表末尾。而extend(x)是将 x 中所有元素逐个添加到原列表末尾,相当于+=x。 - 复制列表不能直接赋值,因为这只会复制引用,使原列表和副本指向同一个对象,此时若修改其中一个,另一个也会跟着改变。应使用切片、构造函数、
copy()创建新的引用,比如复制列表 my_list 应该用my_list[:]、list(my_list)或my_list.copy()。若 my_list 含嵌套对象则需使用deepcopy()完成深拷贝,即deepcopy(my_list),该函数需导入 copy 模块。 - 只有一个元素的元组,括号内仅包含一个元素是不够的。我们将需要一个逗号结尾来表明它实际上是一个元组,比如
x=(1,),如果没有逗号,则 x 为 int 类型。 - set 的元素和 dict 的 key 必须是不可变类型。
- zip 对象是一个迭代器,一次用完就耗尽(exhausted):当 zip 对象转为 list、dict 等类型后,其内部指针会移动到末尾,没有剩余元素。若之后再将原 zip 对象进行类型转换,得到的只能是空对象(空列表,空字典等)。
- 函数中的任意数量的参数都可以具有默认值。但是一旦有了默认参数,其右边的所有参数也必须具有默认值。
- 我们可以在函数调用期间将位置参数与关键字参数混合。但是我们必须记住,关键字参数必须跟在所有位置参数之后。
- 只要子类定义了自己的
__init__方法,且需要使用父类在其__init__方法中定义的属性,就必须在子类的__init__中使用 调用父类的 ,否则父类的初始化逻辑不会执行,相关属性将不存在。

