陷阱8:不要使用and
和or
运算符来实现条件赋值
and
和or
运算符是Python中的逻辑运算符,它们可以用来连接两个布尔值,返回一个布尔值。- 有时候,我们需要根据一个条件,来给一个变量赋不同的值,比如根据一个数的正负,来给它取绝对值或相反数。
- 但是,如果我们使用
and
和or
运算符来实现条件赋值,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
# 定义一个函数,用于根据一个数的正负,来给它取绝对值或相反数
def transform(number):
return number > 0 and number or -number # 使用and和or运算符来实现条件赋值
# 调用函数,期望得到 5
print(transform(5)) # 5
# 调用函数,期望得到 -6
print(transform(-6)) # -6
# 调用函数,期望得到 0
print(transform(0)) # 0
为什么会出错呢?
- 因为
and
和or
运算符并不是用来实现条件赋值的,它们的返回值并不一定是布尔值,而是根据短路求值的规则,返回第一个确定结果的值。 - 短路求值的规则是这样的:
- 对于
and
运算符,如果第一个值是False
,那么就返回第一个值,否则返回第二个值。 - 对于
or
运算符,如果第一个值是True
,那么就返回第一个值,否则返回第二个值。
- 这就导致了一些非零的数,使用
and
和or
运算符来实现条件赋值,会得到正确的结果,因为它们的布尔值都是True
,所以会返回期望的值。 - 但是,对于零,使用
and
和or
运算符来实现条件赋值,会得到错误的结果,因为它的布尔值是False
,所以会返回错误的值。
正确的代码
# 定义一个函数,用于根据一个数的正负,来给它取绝对值或相反数
def transform(number):
return number if number > 0 else -number # 使用条件表达式来实现条件赋值
# 调用函数,期望得到 5
print(transform(5)) # 5
# 调用函数,期望得到 -6
print(transform(-6)) # -6
# 调用函数,期望得到 0
print(transform(0)) # 0
陷阱9:不要使用try-except
语句来忽略错误
try-except
语句是Python中的一种异常处理机制,它可以让我们在遇到错误时,执行一些恢复或处理的操作,而不是让程序崩溃。- 有时候,我们可能觉得某些错误不重要,或者不想让用户看到错误信息,就会使用
try-except
语句来忽略错误,比如使用一个空的except
子句。 - 但是,如果我们使用
try-except
语句来忽略错误,就可能导致一些严重的问题,甚至引发错误。
错误的代码
# 定义一个函数,用于打开一个文件,并读取其中的内容
def read_file(filename):
try:
f = open(filename, "r") # 尝试打开文件
content = f.read() # 读取文件内容
f.close() # 关闭文件
return content # 返回文件内容
except: # 使用一个空的except子句来忽略错误
pass
# 调用函数,期望得到 "Hello, world!"
print(read_file("hello.txt")) # Hello, world!
# 调用函数,期望得到 None
print(read_file("not_exist.txt")) # None
为什么会出错呢?
- 因为使用一个空的
except
子句来忽略错误,会捕获所有的异常,不管它们是不是我们想要处理的,这就可能导致一些严重的问题,比如:
- 我们无法区分不同类型的异常,无法针对不同的情况进行恰当的处理,比如文件不存在、文件无法打开、文件格式错误等。
- 我们无法得到异常的详细信息,无法知道错误的原因和位置,无法进行调试和修复。
- 我们无法抛出异常,让上层的代码或用户知道发生了什么,无法进行适当的反馈或处理。
- 例如,上面的代码中,如果我们传入一个不存在的文件名,就会触发一个
FileNotFoundError
异常,但是我们使用了一个空的except
子句来忽略错误,所以我们无法知道发生了什么,也无法告诉用户文件不存在,只是返回了一个None
值,这就会让用户感到困惑和沮丧。
正确的代码
# 定义一个函数,用于打开一个文件,并读取其中的内容
def read_file(filename):
try:
f = open(filename, "r") # 尝试打开文件
content = f.read() # 读取文件内容
f.close() # 关闭文件
return content # 返回文件内容
except FileNotFoundError: # 指定要捕获的异常类型
print("File not found: " + filename) # 打印异常信息
return None # 返回None值
# 调用函数,期望得到 "Hello, world!"
print(read_file("hello.txt")) # Hello, world!
# 调用函数,期望得到 None
print(read_file("not_exist.txt")) # File not found: not_exist.txt
# None
陷阱10:不要使用==
运算符来比较浮点数
- 浮点数是Python中的一种数据类型,它可以表示小数或科学计数法的数值,比如
3.14
或1.23e-4
。 - 浮点数是用二进制的方式来存储和计算的,这就意味着,一些十进制的小数,可能无法用二进制的方式精确地表示,而是会有一些舍入误差,比如
0.1
在二进制中就是一个无限循环的小数。 - 有时候,我们需要比较两个浮点数是否相等,比如判断一个数是否是零,或者一个计算结果是否符合预期。
- 但是,如果我们使用
==
运算符来比较浮点数,就可能导致一些意想不到的结果,甚至引发错误。
错误的代码
# 定义一个函数,用于计算一个数的平方根
def sqrt(x):
# 使用牛顿法,不断迭代,直到找到一个近似的解
guess = x / 2 # 初始猜测值
while True:
new_guess = (guess + x / guess) / 2 # 计算新的猜测值
if new_guess == guess: # 使用==运算符来判断是否收敛
break # 如果收敛,就跳出循环
guess = new_guess # 如果没有收敛,就更新猜测值
return guess # 返回近似的解
# 调用函数,期望得到 2
print(sqrt(4)) # 2.0
# 调用函数,期望得到 3
print(sqrt(9)) # 3.0
# 调用函数,期望得到 5
print(sqrt(25)) # 5.0
为什么会出错呢?
- 因为使用
==
运算符来比较浮点数,会要求两个数完全相等,也就是没有任何的误差,这在计算机中是很难实现的,因为浮点数的表示和计算都可能有一些舍入误差,导致两个数在某些位上有微小的差异。 - 这就导致了一些浮点数,使用
==
运算符来比较,会返回False
,即使它们的值是非常接近的,这就会影响我们的逻辑判断,比如判断一个数是否是零,或者一个计算结果是否符合预期。 - 例如,上面的代码中,如果我们传入一个不是完全平方数的数,比如
2
,就会发现,函数无法收敛,而是陷入了无限循环,因为new_guess
和guess
永远不会完全相等,总会有一些微小的误差,导致==
运算符返回False
。
正确的代码
# 定义一个函数,用于计算一个数的平方根
def sqrt(x):
# 使用牛顿法,不断迭代,直到找到一个近似的解
guess = x / 2 # 初始猜测值
epsilon = 1e-6 # 定义一个误差范围
while True:
new_guess = (guess + x / guess) / 2 # 计算新的猜测值
if abs(new_guess - guess) < epsilon: # 使用abs函数和误差范围来判断是否收敛
break # 如果收敛,就跳出循环
guess = new_guess # 如果没有收敛,就更新猜测值
return guess # 返回近似的解
# 调用函数,期望得到 2
print(sqrt(4)) # 2.0
# 调用函数,期望得到 3
print(sqrt(9)) # 3.0
# 调用函数,期望得到 5
print(sqrt(25)) # 5.0
# 调用函数,期望得到 1.414213
print(sqrt(2)) # 1.414213562373095