PEP 290 – 代码迁移和现代化
- 作者:
- Raymond Hettinger <python at rcn.com>
- 状态:
- 活跃
- 类型:
- 信息性
- 创建日期:
- 2002年6月6日
- 发布历史:
摘要
本 PEP 是一组用于在安装新版 Python 时更新 Python 应用程序的程序和思想。
迁移提示强调了可能存在不兼容的区域,并就如何查找和解决这些差异提出了建议。现代化程序展示了如何更新旧代码以利用新的语言特性。
基本原理
此程序库作为已知迁移问题和解决这些问题的程序的目录或清单。
迁移问题可能出于多种原因。一些过时的功能根据 PEP 4 中的指南缓慢废弃。此外,一些代码依赖于未文档化的行为,这些行为可能会在版本之间发生变化。一些代码可能依赖于后来被证明是错误的行为,并且当错误修复后该行为会发生变化。
当新版本的 Python 添加了新功能,使其比以前更清晰或性能更高时,就会出现现代化选项。
新条目的指南
具有提交权限的开发人员可以直接更新此 PEP。其他人可以将其想法发送给开发人员以供考虑。
虽然一致的格式使存储库更易于使用,但请随意添加或删除部分以提高清晰度。
可以提供 Grep 模式作为工具,帮助维护人员查找可能需要更新的代码。但是,不建议使用完全自动化的搜索/替换样式正则表达式。相反,每个代码片段都应该单独评估。
禁忌部分是新条目最重要的部分。它列出了不应应用更新的已知情况。
迁移问题
比较运算符不是产生0或1的快捷方式
在 Python 2.3 之前,比较操作返回 0 或 1 而不是 True 或 False。一些代码可能将其用作在布尔对应项不适用的地方生成零或一的快捷方式。例如
def identity(m=1):
"""Create and m-by-m identity matrix"""
return [[i==j for i in range(m)] for j in range(m)]
在 Python 2.2 中,调用 identity(2) 将产生
[[1, 0], [0, 1]]
在 Python 2.3 中,相同的调用将产生
[[True, False], [False, True]]
由于布尔值是整数的子类,矩阵将继续正常计算,但它不会按预期打印。列表推导式应更改为
return [[int(i==j) for i in range(m)] for j in range(m)]
当存储的数据将由可能期望数字而不是 True 或 False 的其他应用程序使用时,也存在类似的担忧。
现代化程序
程序按利用现代化所需的 Python 版本进行分组。
Python 2.4 或更高版本
在列表开头插入和弹出
Python 的列表实现为在右侧进行追加和弹出时性能最佳。使用 pop(0) 或 insert(0, x) 会导致整个列表的 O(n) 数据移动。为了帮助解决此需求,Python 2.4 引入了一个新容器 collections.deque(),它在左侧和右侧都有高效的追加和弹出操作(权衡是 getitem/setitem 访问速度慢得多)。新容器对于实现数据队列特别有用
模式
c = list(data) --> c = collections.deque(data)
c.pop(0) --> c.popleft()
c.insert(0, x) --> c.appendleft()
定位
grep pop(0 or
grep insert(0
简化自定义排序
在 Python 2.4 中,列表的 sort 方法和新的 sorted 内置函数都接受一个用于计算排序键的 key 函数。与应用于每个比较的 cmp 函数不同,key 函数只应用于每个记录一次。它比 cmp 快得多,并且通常更具可读性,同时代码更少。key 函数还保持了排序的稳定性(具有相同键的记录保持其原始顺序)。
使用比较函数的原始代码
names.sort(lambda x,y: cmp(x.lower(), y.lower()))
带有显式装饰的替代原始代码
tempnames = [(n.lower(), n) for n in names]
tempnames.sort()
names = [original for decorated, original in tempnames]
使用 key 函数的修订代码
names.sort(key=str.lower) # case-insensitive sort
定位: grep sort *.py
替换 Lambda 的常见用法
在 Python 2.4 中, operator 模块获得了两个新函数 itemgetter() 和 attrgetter(),它们可以替换 lambda 关键字的常见用法。新函数运行速度更快,并且被一些人认为提高了可读性。
模式
lambda r: r[2] --> itemgetter(2)
lambda r: r.myattr --> attrgetter('myattr')
典型上下文
sort(studentrecords, key=attrgetter('gpa')) # set a sort field
map(attrgetter('lastname'), studentrecords) # extract a field
定位: grep lambda *.py
简化的反向迭代
Python 2.4 引入了 reversed 内置函数用于反向迭代。现有的反向迭代方法存在冗长、性能问题(速度和内存消耗)和/或缺乏清晰度的问题。首选的风格是向前方向表达序列,将 reversed 应用于结果,然后遍历生成的快速、内存友好的迭代器。
用半开区间表示的原始代码
for i in range(n-1, -1, -1):
print seqn[i]
分多步反转的替代原始代码
rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
print value
用扩展切片表示的替代原始代码
for value in seqn[::-1]:
print value
使用 reversed 函数的修订代码
for value in reversed(seqn):
print value
Python 2.3 或更高版本
测试字符串成员
在 Python 2.3 中,对于 string2 in string1,对 string2 的长度限制被取消;它现在可以是任意长度的字符串。当搜索子字符串时,如果您不关心子字符串在原始字符串中的位置,使用 in 运算符可以使含义更清晰。
模式
string1.find(string2) >= 0 --> string2 in string1
string1.find(string2) != -1 --> string2 in string1
用直接函数调用替换 apply()
在 Python 2.3 中,apply() 被标记为“待定废弃”,因为它因 Python 1.6 引入函数调用中的 * 和 ** 而变得过时。使用直接函数调用总是比 apply() 快一点,因为它省去了内置函数的查找。现在,由于 apply() 使用了 warnings 模块,它甚至更慢了。
模式
apply(f, args, kwds) --> f(*args, **kwds)
注意:apply() 的“待定废弃”在 Python 2.3.3 中被移除,因为它给需要维护与 Python 1.5.2 甚至更早版本兼容的代码的人带来了麻烦,因为当时没有 apply() 的替代方案。然而,该函数仍然被废弃。
Python 2.2 或更高版本
测试字典成员
要测试字典成员,请使用“in”关键字而不是“has_key()”方法。结果更短、更具可读性。这种风格与列表成员测试保持一致。结果略快,因为 has_key 需要属性搜索并使用相对昂贵的函数调用。
模式
if d.has_key(k): --> if k in d:
禁忌
- 某些类似字典的对象可能未定义
__contains__()方法if dictlike.has_key(k)
定位: grep has_key
遍历字典
使用新的 iter 方法遍历字典。iter 方法更快,因为它们不必创建包含所有键、值或项的完整副本的新列表对象。根据需要仅选择键、值或项(键/值对)可以节省创建一次性对象引用的时间,并且在项的情况下,可以节省第二次键的哈希查找。
模式
for key in d.keys(): --> for key in d:
for value in d.values(): --> for value in d.itervalues():
for key, value in d.items():
--> for key, value in d.iteritems():
禁忌
- 如果您需要一个列表,请不要更改返回类型
def getids(): return d.keys()
- 某些类似字典的对象可能未定义
iter方法for k in dictlike.keys():
- 迭代器不支持切片、排序或其他操作
k = d.keys(); j = k[:]
- 字典迭代器禁止修改字典
for k in d.keys(): del[k]
stat 方法
用新的 os.stat 属性和方法替换 stat 常量或索引。os.stat 属性和方法不依赖于顺序,并且不需要导入 stat 模块。
模式
os.stat("foo")[stat.ST_MTIME] --> os.stat("foo").st_mtime
os.stat("foo")[stat.ST_MTIME] --> os.path.getmtime("foo")
定位: grep os.stat 或 grep stat.S
减少对 types 模块的依赖
types 模块未来可能会被废弃。请改用内置构造函数。它们可能稍微快一些。
模式
isinstance(v, types.IntType) --> isinstance(v, int)
isinstance(s, types.StringTypes) --> isinstance(s, basestring)
此技术的完全使用需要 Python 2.3 或更高版本(basestring 在 Python 2.3 中引入),但 Python 2.2 足以满足大多数用途。
定位: grep types *.py | grep import
避免变量名与 __builtins__ 模块冲突
在 Python 2.2 中,为 dict 和 file 添加了新的内置类型。脚本应避免分配掩盖这些类型的变量名。同样的建议也适用于现有的内置函数,如 list。
模式
file = open('myfile.txt') --> f = open('myfile.txt')
dict = obj.__dict__ --> d = obj.__dict__
定位: grep 'file ' *.py
Python 2.1 或更高版本
whrandom 模块已废弃
所有与随机相关的方法都已收集到一个地方,即 random 模块。
模式
import whrandom --> import random
定位: grep whrandom
Python 2.0 或更高版本
字符串方法
string 模块未来可能会被废弃。请改用字符串方法。它们也更快。
模式
import string ; string.method(s, ...) --> s.method(...)
c in string.whitespace --> c.isspace()
定位: grep string *.py | grep import
startswith 和 endswith 字符串方法
请使用这些字符串方法而不是切片。无需创建切片,也没有误算的风险。
模式
"foobar"[:3] == "foo" --> "foobar".startswith("foo")
"foobar"[-3:] == "bar" --> "foobar".endswith("bar")
atexit 模块
atexit 模块支持在程序终止时执行多个函数。此外,它还支持带参数的函数。不幸的是,它的实现与 sys.exitfunc 属性冲突,sys.exitfunc 属性只支持单个退出函数。依赖 sys.exitfunc 的代码可能会干扰其他模块(包括库模块),这些模块选择使用更新、更通用的 atexit 模块。
模式
sys.exitfunc = myfunc --> atexit.register(myfunc)
Python 1.5 或更高版本
基于类的异常
字符串异常已废弃,因此请从 Exception 基类派生。与过时的字符串异常不同,类异常都从另一个异常或 Exception 基类派生。这允许有意义地分组异常。它还允许“except Exception”子句捕获所有异常。
模式
NewError = 'NewError' --> class NewError(Exception): pass
定位:使用 PyChecker。
所有 Python 版本
测试 None
由于只有一个 None 对象,因此可以使用身份进行相等性测试。身份测试比相等性测试略快。此外,某些对象类型可能会重载比较,因此相等性测试可能会慢得多。
模式
if v == None --> if v is None:
if v != None --> if v is not None:
定位: grep '== None' 或 grep '!= None'
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0290.rst