Python 初学者最容易踩的 5 个坑及解决方法
·
引言
在python的学习过程中我踩过不少的坑,我想许多像我一样的新人也可能遇到过这些问题,为此我总结了5个很经典的问题,并给出了我自己的解决办法,希望能对初学者有所帮助。
坑 1:‘is’ 与 ‘==’ 之间的区别
1. 示例代码:
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True
print(a is b) # False
2. 原因解释:
- 其中‘==’比较的是两个列表中的值是否相等。
- 而‘is’则比较的是两个列表是否指向同一块内存。
3. 正确使用:多数情况下我们只需要使用‘==’即可,只有那种需要确认对象标识时才用‘is’。
坑 2:函数默认参数的陷阱
1. 错误示例代码:
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] 不是 [2]
2. 原因解释:Python里定义函数时默认参数只会执行一次。
3. 正确写法:
def add_item(item):
my_list = []
my_list.append(item)
# 可简写为一下形式(多步列表初始化可被替换为列表字面量)
# my_list = [item]
return my_list
print(add_item(1)) # [1]
print(add_item(2)) # [2]
坑 3:深拷贝 vs 浅拷贝
1. 错误实例代码:
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a)
b[0][0] = 99
print(a) # [[99, 2], [3, 4]]
2. 原因解释:以上写法为浅拷贝,浅拷贝只会复制外层对象,而内层对象仍然被共享使用,因为我们不想改变a中的数据,所以则需要使用一下深拷贝的写法。
3. 正确写法:
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[0][0] = 99
print(a) # [[1, 2], [3, 4]]
4. 通俗理解: 引用当时讲师的白话就是:‘深拷贝拷贝的深,浅拷贝拷贝的浅’,仔细思考一下还是比较好理解的。
坑 4:浮点数精度问题
1. 错误实例代码:
print(0.1 + 0.2) # 0.30000000000000004
2. 原因解释:浮点数在计算机中是二进制近似存储,存在精度问题,但python为我们提供了一个decimal模块用来以10进制方式精确计算小数。
3. 正确写法:
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))
# 注意: Decimal()方法中的数值一定要用''包裹.
坑 5: 循环变量的延迟绑定问题 (这个坑是困扰我比较久的我会详细解释一下)
1. 错误实例代码:
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # 输出: [2, 2, 2]
2. 原因解释:
- 简短解释:在python中闭包捕获的是变量的引用,而并非当时的值,也就是说lambda中的‘i’并不会在创建lambda的时候储存下来当数值,而是在lambda被调用时才取‘i’的当前值。
- 关键点解释:
- 定义阶段:每执行 ‘lambda:i’ 时创建一个函数对象,这个函数会引用外层作用域里的名字‘i’(不是复制‘i’的当前值),这些函数内部都持有对同一个名字 ‘i’ 的引用。
- 循环结束后:外层的’i‘最终为2即最后循环的值。
- 调用阶段:当 'f()' 时,lambda去读取‘i’的值,此时‘i’已经是2了,因此所有的lambda都返回2。
- 总结:lambda捕获的是‘对名字i的引用’,而不是‘当时的数值’。
3. 解决方式:其中使用‘i=i’就是在函数定义时把 ‘i’ 的值当作默认值保存下来,这样‘i’就相当于被‘冻结’起来了。
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]
4. 内存图解释:
- 错误写法:三个lambda都只存了1个指针,指向同一个变量‘i’,当循环结束后这个变量最终值为2,所以这个被三个lambda同时使用的指针指向的值就是这一个2.
第一次循环: i = 0
funcs[0] = lambda: i ---> 引用同一个 i
第二次循环: i = 1
funcs[1] = lambda: i ---> 还是引用同一个 i
第三次循环: i = 2
funcs[2] = lambda: i ---> 还是引用同一个 i
┌─────────────┐
│ funcs[0] ├───┐
└─────────────┘ │
┌─────────────┐ │ ┌─────────────┐
│ funcs[1] ├───┼──▶ │ i = 2 │
└─────────────┘ │ └─────────────┘
┌─────────────┐ │
│ funcs[2] ├───┘
└─────────────┘
- 正确写法:每次循环都会生成一个新的‘i’,把当时的值‘冻结’下来,这时每个函数的内部都保留了自己的‘i’,所以输出就是[0, 1, 2]。
┌─────────────┐
│ funcs[0] ├──▶ i(默认参数)=0
└─────────────┘
┌─────────────┐
│ funcs[1] ├──▶ i(默认参数)=1
└─────────────┘
┌─────────────┐
│ funcs[2] ├──▶ i(默认参数)=2
└─────────────┘
更多推荐
所有评论(0)