Python 中的常见陷阱

本文探讨了Python编程中的一些常见陷阱,包括函数内部变量作用域、局部变量定义、闭包与循环、列表和字典操作的副作用以及可变对象作为默认参数的影响。此外,还解释了`finally`块中返回值的优先级。了解这些陷阱有助于避免编程错误,提高代码质量。
摘要由CSDN通过智能技术生成

每一种语言都有各自的场景, 各自的设计, 有各自的坑.

近几年来使用场景越来越广泛的 Python, 都有哪些常见的坑(陷阱).  下面是我从初学以来, 碰到过的陷阱(跟其他语言不一样的地方). 

 

1.函数内的变量作用域

函数内部, 代码块之内定义的变量,代码块之外可以访问。

i = 10

if i>5:
    square = i*i

print(square)

在 Java/C#/C++/C  之类的语言中, 类似上面的代码逻辑会跑错, 因为 代码运行到最后一行的时候, square 出来作用域.

但在 python 里面, 这个代码是能够正常工作的. 在 if/for 代码块之外, 变量可以被访问到.

 

2.函数内部有做赋值的变量, 必须在函数内定义

a = 1

def works():
    print(a)

def fail():
    print(a)
    a = 2


python读入并编译这段代码时,发现def里面有一个对a赋值的语句,然后决定a在函数中属于本地变量名。

那么当print(a)执行时,就会对print(a)按照LEGB原则执行搜索,发现a属于本地变量名但还未赋值。出现变量未赋值前使用的问题。

解决方案:

def work_with_global_var():
    global a
    print(a)
    a = 2

 

3.嵌套在循环中的lambda

func_list = []
for i in range(5):
    func_list.append(lambda x: x + i)


这样生成了一个元素为函数的列表,其中每个函数都可以传入一个参数,然后返回的是传入值和0,1,2,3,4的和。
当然理想情况下是这样,现实并不会如此。

func_list[0](1)
5

func_list[1](1)
5

func_list[2](1)
5

3次调用,每次调用的结果都是4+1的和。
发生这种现象的原因在于变量i在函数调用时才进行查找,而此时循环已经结束,i=4,所以所有函数对象在被调用时参数i全部都为5.
避免出问题的方法:不使用闭包, 或者在 循环体内调用闭包函数(避免 lambda 延迟调用)

funcs = []
results = []

for x in range(7):
    def some_func():
        return x
    funcs.append(some_func)
    results.append(some_func()) # 注意这里函数被执行了

funcs_results = [func() for func in funcs]

print(results)
print(funcs_results)

# 输出:[0, 1, 2, 3, 4, 5, 6]
# 输出:[6, 6, 6, 6, 6, 6, 6]

即使每次在迭代中some_func中的x值都不相同,所有的函数还是都返回6.

 

4.集合的 append(), update() 等操作 不返回对象(原地修改变量)

some_list = [1, 2, 3]
some_dict = {
  "key_1": 1,
  "key_2": 2,
  "key_3": 3
}

some_list = some_list.append(4)
some_dict = some_dict.update({"key_4": 4})

print(some_list)
print(some_dict)

# 输出:None
# 输出:None

 

这是一个写法错误,并不是特殊用法。因为列表和字典的操作函数,比如list.append、list.extend、dict.update等都是原地修改变量,不创建也不返还新的变量

 

 

5.可变对象作函数默认参数

   对于可变对象参数而言,当函数被调用并对该参数进行原地修改,默认参数将发生变化并进而影响接下来的函数调用。
 先用可变对象作为默认参数编写一个函数。
 

def test(a=[]):
    a.append(1)
    print(a)


 接下来多次调用test函数,你一定以为每次打出来的都是一个包含1的列表。但是,事实并不是这样。

test()
[1]


test()

[1, 1]


test()
[1, 1, 1]


标准解决方案:

def test(a=None):
    if a is None:
       a = []
    a.append(1)
    print(a)

6.finally 中有返回时, 以 finally的返回为准

def some_func():
    try:
        return 'from_try'
    finally:
        return 'from_finally'
some_func()
# 始终输出:from_finally


函数的返回值由最后执行的 return 语句决定. 由于 finally 子句一定会执行, 所以 finally 子句中的 return 将始终是最后执行的语句

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值