我教Python已经好几年了。每次讲到列表推导式,总能看到学生皱眉头。这东西看着确实简洁,但读起来像密码一样。你花半小时写出来的推导式,第二天自己都看不懂。
先承认一个事实:普通程序员每天写的最多的是简单逻辑。那些嵌套循环、条件过滤的推导式,看着很帅,调试起来很痛苦。你写的代码不是给机器看的,是给人看的。人看不懂的代码,再快也没用。
第一替代方案:用普通循环拆开来写。
很多人觉得循环啰嗦。但循环是最直白的表达方式。比如你要从列表里挑出所有大于10的数字,再乘以2。列表推导式写出来是这样:
[x2 for x in numbers if x > 10]
看着不错是吧。那要是再加个条件呢?比如还要判断x是不是偶数。有人说可以再套一层if。没问题,你写:
[x2 for x in numbers if x > 10 if x % 2 == 0]
多了一个if,但读代码的人要停下来想:这俩if是并列还是嵌套?其实它们是并列关系。用循环写就清楚多了:
result = []
for x in numbers:
if x > 10 and x % 2 == 0:
result.append(x2)
一眼就能看明白。多写两行代码,省下半小时查bug的时间,划算。
第二替代方案:用map和filter组合。
有些人觉得循环还是太啰嗦。那就用内置函数。map和filter已经是Python社区的老朋友了。它们的可读性比推导式好很多。比如要把列表里每个字符串转成大写,并滤掉长度小于3的:
list(map(str.upper, filter(lambda x: len(x) >= 3, names)))
有人说这读起来像函数式编程,其实比推导式好理解。map表示我要对每个元素做操作,filter表示我要筛选。动词在前,名词在后,符合说话习惯。不信你对比下推导式:
[name.upper() for name in names if len(name) >= 3]
推导式把操作放在前面,条件放在后面。读的时候要倒着看:先看到name.upper(),再看到for name in names,最后才看到if条件。脑子得转个弯。map和filter可以顺着读:先filter筛选,再map处理。
第三替代方案:直接写个普通函数。
如果逻辑再复杂一点,比如要根据不同的条件决定要不要处理,处理方式还不一样。这时候别管什么推导式了,直接写个函数。函数名就是最好的注释。
举个例子,你有个订单列表,需要计算打折后的价格。普通会员打9折,vip会员打8折,满200减30。用推导式写会变成一坨:
[price 0.9 if member == 'normal' else price 0.8 if member == 'vip' else price - 30 for price, member in orders if price > 0]
这种代码放到项目里,同事看到会想打人。你不如写个函数:
def calc_discount(price, member):
if price <= 0:
return price
if member == 'normal':
return price 0.9
if member == 'vip':
return price 0.8
return price - 30
result = [calc_discount(price, member) for price, member in orders]
核心逻辑放在函数里。外面用一个简单的循环或者推导式调用函数。这样每个部分都清楚。函数负责计算,循环负责遍历。以后需求变了,改函数就行。测试也好测。
列表推导式不是不能用。简单的场合,比如把列表里每个数字加1,[x+1 for x in numbers] 没问题。三个条件以上,或者涉及多种处理方式,就别硬套了。
写代码是写给下一个维护你的人看的,那个人可能是三个月后的你自己。
你不想让自己头疼吧?那就选最直白的方式。Python的优雅不在于写多短的代码,在于写着明白,读着舒服。