编写条件分支代码的技巧。
规范使用 python 中的条件判断语句
- 1. 避免多层分支嵌套
- 2. 封装过于复杂的逻辑判断
- 3. 不同分支下的重复代码
- 4. 合理使用三元表达式
- 常见技巧。
- 1.德摩根定律
- 2.自定义类的魔法方法
- 3. 在条件判断中使用 all() / any()
- 4. 使用 try/while/for 中 else 分支
- 常见陷阱
- 1. 与 None 值得比较
- 2. and 和 or 的运算优先级
- Reference
1. 避免多层分支嵌套
Python中利用缩进来替代 { }。如果多个 if 嵌套, 堪称 ” 嵌套 if 地狱 “
下面的代码直接翻译了原始条件分支,导致代码可读性和维护性很差。
def buy_fruit(nerd, store):
"""去水果店买苹果
- 先得看看店是不是在营业
- 如果有苹果的话,就买 1 个
- 如果钱不够,就回家取钱再来
"""
if store.is_open():
if store.has_stocks("apple"):
if nerd.can_afford(store.price("apple", amount=1)):
nerd.buy(store, "apple", amount=1)
return
else:
nerd.go_home_and_get_money()
return buy_fruit(nerd, store)
else:
raise MadAtNoFruit("no apple in store!")
else:
raise MadAtNoFruit("store is closed!")
我们利用取反的方式,“提前结束” 来优化这段代码:
def buy_fruit(nerd, store):
if not store.is_open():
raise MadAtNoFruit("store is closed!")
if not store.has_stocks("apple"):
raise MadAtNoFruit("no apple in store!")
if nerd.can_afford(store.price("apple", amount=1)):
nerd.buy(store, "apple", amount=1)
return
else:
nerd.go_home_and_get_money()
return buy_fruit(nerd, store)
“提前结束” :指在函数内使用 return 或 raise 等语句提前在分支内结束函数。
利用逆向思维,当分支条件不满足时,我们直接结束这段代码,这样更容易阅读。
2. 封装过于复杂的逻辑判断
如果条件分支中有过多的判断条件 and | not | or, 可以将这样的部分封装起来。
if person.is_student and person.age > 20 and person.is_male:
pass
这样封装的部分更有可解释性,更容易被人理解。
最重要的事还解决了,相同代码多次出现的问题。
if person.identity() and person.gender():
pass
3. 不同分支下的重复代码
下面的代码很难让人直观分别出不同。
if person.is_student():
record_imformation(
name = person.name,
age = person.name,
address = person.address,
student_number = 10011,
recorded = now(),
)
else:
update_information(
name = person.name,
age = person.name,
address = person.address,
updated = now(),
)
关注这些由分支产生的重复代码块,通过转化简化它们。
if person.is_student():
imformation_func = record_imformation
extra_args = {'student_number' : 10011, 'recorded' : now() }
else:
imformation_func = update_information
extra_args = {'updated' : now() }
information_func(
name = person.name,
age = person.name,
address = person.address,
**extra_args
)
4. 合理使用三元表达式
使用普通的 if / else 语句 代码可读性通常更好。
对于三元表达式只处理简单的逻辑分支即可。
language = "python" if you.favor("dynamic") else "golang"
常见技巧。
1.德摩根定律
对于下面的代码,很难第一时间 get 到逻辑关系。
# 如果用户没有登录或者用户没有使用 chrome,拒绝提供服务
if not user.has_logged_in or not user.is_from_chrome:
return "our service is only available for chrome logged in user"
而使用德摩根定律。
not A or not B = not (A and B), 代码读起来会容易很多。
if not (user.has_logged_in and user.is_from_chrome):
return "our service is only available for chrome logged in user"
2.自定义类的魔法方法
python提供了跟多自定义类的魔法方法,我们可以利用它门,让我们的代码更加pythonic。
下面的代码用到了 len() 函数。
class UserCollection(object):
def __init__(self, users):
self._users = users
users = UserCollection([piglei, raymond])
if len(users._users) > 0:
print("There's some users in collection!")
通过给类自定义魔法方法,分支条件变得更加简单。
并且可以自己控制魔法方法的返回值。
class UserCollection:
def __init__(self, users):
self._users = users
def __len__(self):
return len(self._users)
users = UserCollection([piglei, raymond])
# 定义了 __len__ 方法后,UserCollection 对象本身就可以被用于布尔判断了
if users:
print("There's some users in collection!")
3. 在条件判断中使用 all() / any()
- all (x) : x 中所有对象都为真时返回 True, 否则 False
- any (x): 只要 x 中一个对象为真时返回 True, 否则 False
def all_numbers_gt_10(numbers):
"""仅当序列中所有数字大于 10 时,返回 True
"""
if not numbers:
return False
for n in numbers:
if n <= 10:
return False
return True
使用all ( )内建函数,再配合生成器表达式。
def all_numbers_gt_10_2(numbers):
return bool(numbers) and all(n > 10 for n in numbers)
4. 使用 try/while/for 中 else 分支
def do_stuff():
first_thing_successed = False
try:
# ...
first_thing_successed = True
except Exception as e:
# ...
return
# 仅当 first_thing 成功完成时,做第二件事
if first_thing_successed:
return do_the_second_thing()
其实,我们可以用更简单的方法达到同样的效果:
def do_stuff():
try:
# ...
except Exception as e:
# ...
return
else:
return do_the_second_thing()
在 try 的语句块后面加上 else 分支。
类似的 for / while 也支持 else 分支。
常见陷阱
1. 与 None 值得比较
在 python 中, == 与 is 两种比较方法有根本的区别。
- == : 仅比较两者的值是否一致
- is : 比较两者是否指向内存中的同一份地址。
但是 None 在 python 中是一个单例对象,如果要判断某个变量是否为 None 要用 is, 只有 is 才严格意义上表示某个变量是否为None
2. and 和 or 的运算优先级
and 的优先级大于 or
即使执行的优先级如我们想要的一致,也要采取额外括号的方式让代码更清晰。
(True or False) and False # False
True or False and False # True
此外,
c and a or b 不是总能给你正确的结果。
只有当 a 与 b 的布尔值为真时,这个表达式才正常工作,因为逻辑运算的短路特性。
Reference
python 工匠