时间:2022-08-24 09:41:05 | 栏目:Python代码 | 点击:次
下面的代码使用数字来作为判断条件的语句,如果你从别人手里接手过这部分代码,很难第一时间理解它的意义。
def mark_trip_as_featured(trip): """将某个旅程添加到推荐栏目 """ if trip.source == 11: do_some_thing(trip) elif trip.source == 12: do_some_other_thing(trip) ... ... return
我们可以使用枚举的方式,对这些数字部分做一些说明。
from enum import IntEnum class TripSource(IntEnum): FROM_WEBSITE = 11 FROM_IOS_CLIENT = 12 def mark_trip_as_featured(trip): if trip.source == TripSource.FROM_WEBSITE: do_some_thing(trip) elif trip.source == TripSource.FROM_IOS_CLIENT: do_some_other_thing(trip) ... ... return
将重复出现的数字定义成枚举类型,不仅改善了代码的可读性,还降低了代码出现 Bug
的机率。
当然不是所有的数字都需要用到枚举说明,像常见数字下标 0 和 -1
就不需要。
“ 裸字符串处理 ” 这里指只使用基本的加减乘除和循环、配合内置函数/方法来操作字符串,获得我们需要的结果。
def fetch_users(conn, min_level=None, gender=None, has_membership=False, sort_field="created"): """获取用户列表 :param int min_level: 要求的最低用户级别,默认为所有级别 :param int gender: 筛选用户性别,默认为所有性别 :param int has_membership: 筛选所有会员/非会员用户,默认非会员 :param str sort_field: 排序字段,默认为按 created "用户创建日期" :returns: 列表:[(User ID, User Name), ...] """ # 一种古老的 SQL 拼接技巧,使用 "WHERE 1=1" 来简化字符串拼接操作 # 区分查询 params 来避免 SQL 注入问题 statement = "SELECT id, name FROM users WHERE 1=1" params = [] if min_level is not None: statement += " AND level >= ?" params.append(min_level) if gender is not None: statement += " AND gender >= ?" params.append(gender) if has_membership: statement += " AND has_membership == true" else: statement += " AND has_membership == false" statement += " ORDER BY ?" params.append(sort_field) return list(conn.execute(statement, params))
这样做虽然看起来简单,符合直觉,但是随着函数逻辑变得复杂,这段代码会变得容易出错。
更好的选择是利用一些开源的对象化模块来操作他们。
这里使用了 SQLAlchemy
。
def fetch_users_v2(conn, min_level=None, gender=None, has_membership=False, sort_field="created"): """获取用户列表 """ query = select([users.c.id, users.c.name]) if min_level is not None: query = query.where(users.c.level >= min_level) if gender is not None: query = query.where(users.c.gender == gender) query = query.where(users.c.has_membership == has_membership).order_by(users.c[sort_field]) return list(conn.execute(query))
其它的替换思路:
Q: 目标/源字符串是结构化的,遵循某种格式吗?
其它的开源的对象化模块。
尝试使用模板引擎而不是复杂字符串处理逻辑来达到目的。
def f1(delta_seconds): # 如果时间已经过去了超过 11 天,不做任何事 if delta_seconds > 950400: return ...
“为什么我们不直接把代码写成 if delta_seconds < 11 * 24 * 3600: 呢?”
“性能”,答案一定会是“性能”。 Python 是一门解释型语言,所以预先计算出 950400 正是因为我们不想让每次对函数 f1 的调用都带上这部分的计算开销。
不过事实是:即使我们把代码改成 if delta_seconds < 11 * 24 * 3600:,函数也不会多出任何额外的开销。
当我们的代码中需要出现复杂计算的字面量时,请保留整个算式吧。它对性能没有任何影响,而且会增加代码的可读性。
def f1(delta_seconds): if delta_seconds < 11 * 24 * 3600: return
True
和 False
可以当成 1 和 0 使用
>>> True + 1 2 >>> 1 / False Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero
计数简化操作。
>>> l = [1, 2, 4, 5, 7] >>> sum(i % 2 == 0 for i in l) 2
如果将某个布尔值表达式作为列表的下标使用,可以实现类似三元表达式的目的:
# 类似的三元表达式:"Javascript" if 2 > 1 else "Python" >>> ["Python", "Javascript"][2 > 1] 'Javascript'
对于字符串我们常使用 \ 和 + 来讲字符串拆分成好几段。
还有一种简单的方法是用 ()。
用 ()括起来就可以随意拆行了。
s = ( "There is something really bad happened during the process. " "Please contact your administrator." )
对于多级缩进字符串:
可以调用其他的标准库来达到简化效果。
from textwrap import dedent def main(): if user.is_active: # dedent 将会缩进掉整段文字最左边的空字符串 message = dedent("""\ Welcome, today's movie list: - Jaw (1975) - The Shining (1980) - Saw (2004)""")
大数字也可以变得更加可阅读:
在数字之间加入下划线。
>>> 10_000_000.0 # 以“千”为单位划分数字 10000000.0 >>> 0xCAFE_F00D # 16进制数字同样有效,4个一组更易读 3405705229 >>> 0b_0011_1111_0100_1110 # 二进制也有效 16206 >>> int('0b_1111_0000', 2) # 处理字符串的时候也会正确处理下划线 240
例如 : .split() 和 .rsplit() 的区别是,一个从左到右分割字符串,另一个是从右到左处理字符串。
合理使用一些现成 string 操作函数可以让工作事半功倍。
float ( " inf " )
和 float ( " -inf ")
,对应着无穷大和无穷小。
float( " -inf ") < 任意数值 < float( " inf ")
一些可以用上的场合。
# A. 根据年龄升序排序,没有提供年龄放在最后边 >>> users = {"tom": 19, "jenny": 13, "jack": None, "andrew": 43} >>> sorted(users.keys(), key=lambda user: users.get(user) or float('inf')) ['jenny', 'tom', 'andrew', 'jack'] # B. 作为循环初始值,简化第一次判断逻辑 >>> max_num = float('-inf') >>> # 找到列表中最大的数字 >>> for i in [23, 71, 3, 21, 8]: ...: if i > max_num: ...: max_num = i ...: >>> max_num 71
如下:这个操作并不是线程安全的。
这个简单的累加语句,会被编译成包括取值和保存在内的好几个不同步骤。
而在多线程环境下,任意一个其他线程都有可能在其中某个步骤切入进来,阻碍你获得正确的结果。
def incr(value): value += 1 # 使用 dis 模块查看字节码 import dis dis.dis(incr) 0 LOAD_FAST 0 (value) 2 LOAD_CONST 1 (1) 4 INPLACE_ADD 6 STORE_FAST 0 (value) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE
常用 dis
模块去验证自己的操作,有时候,结果和我们预想的并不一样。
Python 的字符串拼接(+=)在 2.2 以及之前的版本确实很慢。
但之后的版本做了更新,效率已经大大提升,所有字符串的拼接还是可以使用的。