查看“︁Python/编程习惯用法”︁的源代码
←
Python/编程习惯用法
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
Python 是一种强惯用法的语言:通常只有一种最佳方式来做某事([[:w:编程习惯用法]]),而不是多种方式:Perl语言的“不只一种方法来做一件事”不是Python的座右铭。 本节从一些一般原则开始,然后介绍该语言,重点介绍如何惯用标准库中的操作、数据类型和模块。 ==原理== 使用 [[Python/异常|exceptions]]进行错误检查,遵循EAFP(请求原谅比请求许可更容易)而不是LBYL(三思而后行):将可能失败的操作放在<code>try...except</code>块中。 使用 [[Python/上下文管理器|上下文管理器]]管理资源,如文件。使用<code>finally</code>进行临时清理,但最好编写上下文管理器来封装它。 使用属性,而不是getter/setter方法。 使用字典来记录动态记录,使用类来记录静态记录(对于简单类,使用 [https://docs.python.org/3.3/library/collections.html#collections.namedtuple collections.namedtuple]):如果记录始终具有相同的字段,则在类中明确说明;如果字段可能有所不同(存在或不存在),请使用字典。 使用 <code>_</code> 表示一次性变量,例如在返回元组时丢弃返回值,或指示忽略参数(例如,当接口需要时)。您可以使用 <code>*_, **__</code>丢弃传递给函数的位置参数或关键字参数:这些参数对应于通常的<code>*args, **kwargs</code>参数,但被明确丢弃。您还可以在位置参数或命名参数(在您使用的参数之后)之外使用这些参数,这样您就可以使用一些参数并丢弃多余的参数。 使用隐式True/False(真/假值),除非需要区分假值,例如 None、0和[],在这种情况下使用显式检查,例如 <code>is None</code> 或 <code>== 0</code>。 在 <code>try、for、while</code> 之后使用可选的 <code>else</code> 子句,而不仅仅是 <code>if</code>。 ==导入== 对于可读且健壮的代码,仅导入模块,而不是名称(如函数或类),因为这会创建一个新的(名称)绑定,而该绑定不一定与现有绑定同步。<ref>“[https://docs.python.org/3/howto/doanddont.html Python中的习语和反习语]”: [https://docs.python.org/3/howto/doanddont.html#from-module-import-name1-name2 from module import name1, name2]</ref> 例如,给定一个定义函数 <code>f</code> 的模块 <code>m</code>,使用 <code>from m import f</code> 导入该函数意味着如果将其中任何一个分配给 <code>m.f</code>和<code>f</code>,则它们可以不同(创建新的绑定)。 实际上,这经常被忽略,特别是对于小规模代码,因为导入后更改模块的情况很少见,所以这很少成为问题,并且类和函数都是从模块导入的,因此可以不带前缀引用它们。但是,对于健壮的大规模代码,这是一条重要规则,因为它可能会产生非常微妙的错误。 对于类型较少的健壮代码,可以使用重命名导入来缩写较长的模块名称: <syntaxhighlight lang=python> import module_with_very_long_name as vl vl.f() # easier than module_with_very_long_name.f, but still robust </syntaxhighlight> 请注意,使用 <code>from</code> 从 [https://docs.python.org/3/tutorial/modules.html#packages 包] 导入子模块(或子包)是完全没问题的: <syntaxhighlight lang=python> from p import sm # completely fine sm.f() </syntaxhighlight> ==运算== ;交换值 <syntaxhighlight lang=python> b, a = a, b </syntaxhighlight> ;在非零值上的属性访问 要访问可能是对象或可能是 <code>None</code> 的值的属性(尤其是调用方法),请使用 <code>and</code> 的布尔快捷方式: <syntaxhighlight lang=python> a and a.x a and a.f() </syntaxhighlight> 对于正则表达式匹配特别有用: <syntaxhighlight lang=python> match and match.group(0) </syntaxhighlight> ;in 使用 <code>in</code> 进行子字符串检查。 ==数据类型== ===所有的序列类型=== ;迭代期间索引 如果您需要跟踪可迭代对象的迭代索引,请使用 <code>enumerate()</code>同时获得索引和值: <syntaxhighlight lang=python> for i, x in enumerate(l): # ... </syntaxhighlight> 反惯用法: <syntaxhighlight lang=python> for i in range(len(l)): x = l[i] # why did you go from list to numbers back to the list? # ... </syntaxhighlight> ;查找第一个匹配元素 Python 序列确实有一个 <code>index</code> 方法,但它会返回序列中特定值第一次出现的索引。要查找满足条件的值的第一次出现,请使用 <code>next</code> 和生成器表达式: <syntaxhighlight lang=python> try: x = next(i for i, n in enumerate(l) if n > 0) except StopIteration: print('No positive numbers') else: print('The index of the first positive number is', x) </syntaxhighlight> 如果您需要的是值,而不是其出现的索引,您可以直接通过以下方式获取它: <syntaxhighlight lang=python> try: x = next(n for n in l if n > 0) except StopIteration: print('No positive numbers') else: print('The first positive number is', x) </syntaxhighlight> 这种构造的原因有两个: * 异常让您发出“未找到匹配项”的信号(它们解决了半谓词问题):由于您返回的是单个值(而不是索引),因此无法在值中返回该值。 * 生成器表达式让您无需 lambda 或引入新语法即可使用表达式。 ;截断 对于可变序列,请使用 <code>del</code>,而不是重新分配给切片: <syntaxhighlight lang=python> del l[j:] del l[:i] </syntaxhighlight> 反惯用法: <syntaxhighlight lang=python> l = l[:j] l = l[i:] </syntaxhighlight> 最简单的原因是 <code>del</code> 明确表明了您的意图:您正在截断。 更微妙的是,切片会创建对同一列表的另一个引用(因为列表是可变的),然后无法访问的数据可以被垃圾收集,但通常这是稍后完成的。相反,删除会立即就地修改列表(这比创建切片然后将其分配给现有变量更快),并允许 Python 立即释放已删除的元素,而不是等待垃圾收集。 在某些情况下,您“确实”想要同一列表的 2 个切片 - 虽然这在基本编程中很少见,除了在 <code>for</code> 循环中对切片进行一次迭代 - 但您很少会想要对整个列表进行切片,然后用切片替换原始列表变量(但不更改另一个切片!),如以下有趣的代码所示: <syntaxhighlight lang=python> m = l l = l[i:j] # why not m = l[i:j] ? </syntaxhighlight> ;来自可迭代对象的排序列表 您可以直接从任何可迭代对象创建排序列表,而无需先创建列表然后对其进行排序。这些包括集合和字典(按键迭代): <syntaxhighlight lang=python> s = {1, 'a', ...} l = sorted(s) d = {'a': 1, ...} l = sorted(d) </syntaxhighlight> ===Tuple=== 使用元组表示常量序列。这很少是必要的(主要是在用作字典中的键时),但可以使意图更明确。 ===字符串=== ;子字符串 使用 <code>in</code> 进行子字符串检查。 但是,不要使用 <code>in</code> 检查字符串是否为单字符匹配,因为它会匹配子字符串并返回虚假匹配 - 而是使用有效值的元组。例如,以下是错误的: <syntaxhighlight lang=python> def valid_sign(sign): return sign in '+-' # wrong, returns true for sign == '+-' </syntaxhighlight> 相反,使用元组: <syntaxhighlight lang=python> def valid_sign(sign): return sign in ('+', '-') </syntaxhighlight> ;构建字符串 要逐步生成长字符串,请构建一个列表,然后使用 <code><nowiki>''</nowiki></code> 将其连接起来 - 如果构建的是文本文件,则使用换行符(在这种情况下不要忘记最后的换行符!)。这比附加到字符串更快更清晰,后者通常很“慢”。(原则上,字符串的总长度和添加次数可以是 <math>O(nk)</math>,如果各部分大小相似,则为 <math>O(n^2)</math>。) 但是,某些版本的 CPython 中有一些优化,可以使简单的字符串附加更快 - CPython 2.5+ 中的字符串附加和 CPython 3.0+ 中的字节串附加都很快,但对于构建 Unicode 字符串(Python 2 中的 unicode,Python 3 中的字符串),连接更快。如果进行广泛的字符串操作,请注意这一点并分析您的代码。有关详细信息,请参阅 [https://wiki.python.org/moin/PythonSpeed/PerformanceTips#String_Concatenation 性能提示:字符串连接] 和 [https://wiki.python.org/moin/ConcatenationTestCode 连接测试代码]。 不要这样做: <syntaxhighlight lang=python> s = '' for x in l: # this makes a new string every iteration, because strings are immutable s += x </syntaxhighlight> 而是要: <syntaxhighlight lang=python> # ... # l.append(x) s = ''.join(l) </syntaxhighlight> 你甚至可以使用非常高效的生成器表达式: <syntaxhighlight lang=python> s = ''.join(f(x) for x in l) </syntaxhighlight> 如果您确实想要一个可变的字符串类对象,您可以使用 <code>StringIO</code>。 * [http://www.skymind.com/~ocrow/python_string/ Efficient String Concatenation in Python] – old article (so benchmarks out of date), but gives overview of some techniques. ===字典类型=== 要遍历字典,可以是键、值,或者两者: <syntaxhighlight lang=python> # Iterate over keys for k in d: ... # Iterate over values, Python 3 for v in d.values(): ... # Iterate over values, Python 2 # In Python 2, dict.values() returns a copy for v in d.itervalues(): ... # Iterate over keys and values, Python 3 for k, v in d.items(): ... # Iterate over values, Python 2 # In Python 2, dict.items() returns a copy for k, v in d.iteritems(): ... </syntaxhighlight> 反模式: <syntaxhighlight lang=python> for k, _ in d.items(): # instead: for k in d: ... for _, v in d.items(): # instead: for v in d.values() ... </syntaxhighlight> 修复: * setdefault * 通常最好使用 collections.defaultdict <code>dict.get</code> 很有用,但使用 <code>dict.get</code> 然后检查它是否为 <code>None</code> 作为测试键是否在字典中的方式是一种反习惯用法,因为 <code>None</code> 是一个潜在值,并且可以直接检查键是否在字典中。但是,如果这不是一个潜在值,则可以使用 <code>get</code> 并与 <code>None</code> 进行比较。 <syntaxhighlight lang=python> if 'k' in d: # ... d['k'] </syntaxhighlight> 反习语(除非 <code>None</code> 不是潜在值): <syntaxhighlight lang=python> v = d.get('k') if v is not None: # ... v </syntaxhighlight> ;来自键和值的并行序列的字典 使用 <code>zip</code> 作为:<code>dict(zip(keys, values))</code> ==模块== ===re=== 如果找到则匹配,否则 <code>无</code>: <syntaxhighlight lang=python> match = re.match(r, s) return match and match.group(0) </syntaxhighlight> ...如果没有匹配则返回 <code>None</code>,如果有匹配则返回匹配内容。 ==参考文献== {{reflist|30em}} {{refbegin}} * “[https://docs.python.org/3.1/howto/doanddont.html Idioms and Anti-Idioms in Python]”, Moshe Zadka ==进一步阅读== * “[http://legacy.python.org/dev/peps/pep-0020/ PEP 20 -- The Zen of Python]”, Tim Peters
该页面使用的模板:
Template:Refbegin
(
查看源代码
)
Template:Reflist
(
查看源代码
)
返回
Python/编程习惯用法
。
导航菜单
个人工具
登录
命名空间
页面
讨论
不转换
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
工具
链入页面
相关更改
页面信息