设为首页 加入收藏

TOP

Python使用数字与字符串的技巧(一)
2019-10-11 11:19:43 】 浏览:159
Tags:Python 使用 数字 字符串 技巧

1.少写数字字面量

“数字字面量(integer literal)” 是指那些直接出现在代码里的数字。它们分布在代码里的各个角落,比如代码 del users[0] 里的 0 就是一个数字字面量。它们简单、实用,每个人每天都在写。但是,当你的代码里不断重复出现一些特定字面量时,你的“代码质量告警灯”就应该亮起黄灯

举个例子,假如你刚加入一家心仪已久的新公司,同事转交给你的项目里有这么一个函数:

def mark_trip_as_featured(trip):
 
    """将某个旅程添加到推荐栏目
 
    """
 
    **if** trip.source== 11:
 
        do_some_thing(trip)
 
    elif trip.source== 12:
 
        do_some_other_thing(trip)
 
    **return**

这个函数做了什么事?你努力想搞懂它的意思,不过 trip.source == 11 是什么情况?那 == 12 呢?这两行代码很简单,没有用到任何魔法特性。但初次接触代码的你可能需要花费一整个下午,才能弄懂它们的含义。

问题就出在那几个数字字面量上。 最初写下这个函数的人,可能是在公司成立之初加入的那位元老程序员。而他对那几个数字的含义非常清楚。但如果你是一位刚接触这段代码的新人,就完全是另外一码事了。

使用 enum 枚举类型改善代码

那么,怎么改善这段代码?最直接的方式,就是为这两个条件分支添加注释。不过在这里,“添加注释”显然不是提升代码可读性的最佳办法(其实在绝大多数其他情况下都不是)。我们需要用有意义的名称来代替这些字面量,而枚举类型(enum)用在这里最合适不过了。

enum 是 Python 自 3.4 版本引入的内置模块,如果你使用的是更早的版本,可以通过 pip install enum34 来安装它。下面是使用 enum 的样例代码:

# -*- coding: utf-8 -*-
 
from **enum** import IntEnum
 
**class** TripSource(IntEum):
 
    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 的几率也会降低。

试想一下,如果你在某个分支判断时将 11 错打成了 111 会怎么样?我们时常会犯这种错,而这类错误在早期特别难被发现。将这些数字字面量全部放入枚举类型中可以比较好的规避这类问题。类似的,将字符串字面量改写成枚举也可以获得同样的好处。

使用枚举类型代替字面量的好处:

· 提升代码可读性:所有人都不需要记忆某个神奇的数字代表什么

· 提升代码正确性:减少打错数字或字母产生 bug 的可能性

当然,你完全没有必要把代码里的所有字面量都改成枚举类型。 代码里出现的字面量,只要在它所处的上下文里面容易理解,就可以使用它。 比如那些经常作为数字下标出现的 0 和 -1 就完全没有问题,因为所有人都知道它们的意思。

2. 别在裸字符串处理上走太远

什么是“裸字符串处理”?在这篇文章里,它指只使用基本的加减乘除和循环、配合内置函数/方法来操作字符串,获得我们需要的结果。

所有人都写过这样的代码。有时候我们需要拼接一大段发给用户的告警信息,有时我们需要构造一大段发送给数据库的 SQL 查询语句,就像下面这样:

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))

我们之所以用这种方式拼接出需要的字符串 – 在这里是 SQL 语句 – 是因为这样做简单、直接,符合直觉。但是这样做最大的问题在于:随着函数逻辑变得更复杂,这段拼接代码会变得容易出错、难以扩展。事实上,上面这段 Demo 代码也只是仅仅做到看上去没有明显的 bug 而已 (谁知道有没有其他隐藏问题)。

其实,对于 SQL 语句这种结构化、有规则的字符串,用对象化的方式构建和编辑它才是更好的做法。下面这段代码用 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!= None:
 
        query= query.where(users.c.level>=
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Django TypeError: render() got .. 下一篇Elasticsearch 7.x - IK分词器插..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目