1575 lines
204 KiB
HTML
1575 lines
204 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_CN">
|
||
<head>
|
||
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>编程常见问题 — Python 3.8.20 文档</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
|
||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script src="../_static/language_data.js"></script>
|
||
<script src="../_static/translations.js"></script>
|
||
|
||
<script src="../_static/sidebar.js"></script>
|
||
|
||
<link rel="search" type="application/opensearchdescription+xml"
|
||
title="在 Python 3.8.20 文档 中搜索"
|
||
href="../_static/opensearch.xml"/>
|
||
<link rel="author" title="关于这些文档" href="../about.html" />
|
||
<link rel="index" title="索引" href="../genindex.html" />
|
||
<link rel="search" title="搜索" href="../search.html" />
|
||
<link rel="copyright" title="版权所有" href="../copyright.html" />
|
||
<link rel="next" title="设计和历史常见问题" href="design.html" />
|
||
<link rel="prev" title="Python常见问题" href="general.html" />
|
||
<link rel="canonical" href="https://docs.python.org/3/faq/programming.html" />
|
||
|
||
|
||
|
||
|
||
|
||
<style>
|
||
@media only screen {
|
||
table.full-width-table {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
<link rel="shortcut icon" type="image/png" href="../_static/py.svg" />
|
||
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
||
<script type="text/javascript" src="../_static/menu.js"></script>
|
||
|
||
</head>
|
||
<body>
|
||
<div class="mobile-nav">
|
||
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
|
||
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu" />
|
||
<label for="menuToggler" class="toggler__label">
|
||
<span></span>
|
||
</label>
|
||
<nav class="nav-content" role="navigation">
|
||
<a href="https://www.python.org/" class="nav-logo">
|
||
<img src="../_static/py.svg" alt="Logo"/>
|
||
</a>
|
||
<div class="version_switcher_placeholder"></div>
|
||
<form role="search" class="search" action="../search.html" method="get">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
|
||
<path fill-rule="nonzero"
|
||
d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#444"></path>
|
||
</svg>
|
||
<input type="text" name="q" aria-label="快速搜索"/>
|
||
<input type="submit" value="转向"/>
|
||
</form>
|
||
</nav>
|
||
<div class="menu-wrapper">
|
||
<nav class="menu" role="navigation" aria-label="main navigation">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<h3><a href="../contents.html">目录</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">编程常见问题</a><ul>
|
||
<li><a class="reference internal" href="#general-questions">一般问题</a></li>
|
||
<li><a class="reference internal" href="#core-language">语言核心内容</a></li>
|
||
<li><a class="reference internal" href="#numbers-and-strings">数字和字符串</a></li>
|
||
<li><a class="reference internal" href="#performance">性能</a></li>
|
||
<li><a class="reference internal" href="#sequences-tuples-lists">序列(元组/列表)</a></li>
|
||
<li><a class="reference internal" href="#objects">对象</a></li>
|
||
<li><a class="reference internal" href="#modules">模块</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>上一个主题</h4>
|
||
<p class="topless"><a href="general.html"
|
||
title="上一章">Python常见问题</a></p>
|
||
<h4>下一个主题</h4>
|
||
<p class="topless"><a href="design.html"
|
||
title="下一章">设计和历史常见问题</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>本页</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">报告 Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.8/Doc/faq/programming.rst"
|
||
rel="nofollow">显示源代码
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="outdated-warning" style="padding: .5em; text-align: center; background-color: #FFBABA; color: #6A0E0E;">
|
||
这个文档所针对的是一个已不再受支持的 Python 旧版本。
|
||
你应当升级版本,并阅读
|
||
<a href="/3/faq/programming.html"> Python 当前稳定版本的文档</a>.
|
||
</div>
|
||
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>导航</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="总目录"
|
||
accesskey="I">索引</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python 模块索引"
|
||
>模块</a> |</li>
|
||
<li class="right" >
|
||
<a href="design.html" title="设计和历史常见问题"
|
||
accesskey="N">下一页</a> |</li>
|
||
<li class="right" >
|
||
<a href="general.html" title="Python常见问题"
|
||
accesskey="P">上一页</a> |</li>
|
||
|
||
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li class="switchers">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<div class="version_switcher_placeholder"></div>
|
||
</li>
|
||
<li>
|
||
|
||
</li>
|
||
<li id="cpython-language-and-version">
|
||
<a href="../index.html">3.8.20 Documentation</a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python 常见问题</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
|
||
<input type="submit" value="转向" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section id="programming-faq">
|
||
<h1><a class="toc-backref" href="#id2">编程常见问题</a><a class="headerlink" href="#programming-faq" title="永久链接至标题">¶</a></h1>
|
||
<div class="contents topic" id="id1">
|
||
<p class="topic-title">目录</p>
|
||
<ul class="simple">
|
||
<li><p><a class="reference internal" href="#programming-faq" id="id2">编程常见问题</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#general-questions" id="id3">一般问题</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#is-there-a-source-code-level-debugger-with-breakpoints-single-stepping-etc" id="id4">Python 有没有提供带有断点、单步调试等功能的源码级调试器?</a></p></li>
|
||
<li><p><a class="reference internal" href="#are-there-tools-to-help-find-bugs-or-perform-static-analysis" id="id5">是否有能帮助寻找漏洞或执行静态分析的工具?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-create-a-stand-alone-binary-from-a-python-script" id="id6">如何由 Python 脚本创建能独立运行的二进制程序?</a></p></li>
|
||
<li><p><a class="reference internal" href="#are-there-coding-standards-or-a-style-guide-for-python-programs" id="id7">是否有 Python 编码标准或风格指南?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#core-language" id="id8">语言核心内容</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value" id="id9">变量明明有值,为什么还会出现 UnboundLocalError?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-are-the-rules-for-local-and-global-variables-in-python" id="id10">Python 的局部变量和全局变量有哪些规则?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result" id="id11">为什么在循环中定义的参数各异的 lambda 都返回相同的结果?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-share-global-variables-across-modules" id="id12">如何跨模块共享全局变量?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-are-the-best-practices-for-using-import-in-a-module" id="id13">导入模块的“最佳实践”是什么?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-are-default-values-shared-between-objects" id="id14">为什么对象之间会共享默认值?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another" id="id15">如何将可选参数或关键字参数从一个函数传递到另一个函数?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-is-the-difference-between-arguments-and-parameters" id="id16">形参和实参之间有什么区别?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-did-changing-list-y-also-change-list-x" id="id17">为什么修改列表 'y' 也会更改列表 'x'?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-write-a-function-with-output-parameters-call-by-reference" id="id18">如何编写带有输出参数的函数(按照引用调用)?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-you-make-a-higher-order-function-in-python" id="id19">如何在 Python 中创建高阶函数?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-copy-an-object-in-python" id="id20">如何复制 Python 对象?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-find-the-methods-or-attributes-of-an-object" id="id21">如何找到对象的方法或属性?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-my-code-discover-the-name-of-an-object" id="id22">如何用代码获取对象的名称?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-s-up-with-the-comma-operator-s-precedence" id="id23">逗号运算符的优先级是什么?</a></p></li>
|
||
<li><p><a class="reference internal" href="#is-there-an-equivalent-of-c-s-ternary-operator" id="id24">是否提供等价于 C 语言 "?:" 三目运算符的东西?</a></p></li>
|
||
<li><p><a class="reference internal" href="#is-it-possible-to-write-obfuscated-one-liners-in-python" id="id25">是否可以用 Python 编写让人眼晕的单行程序?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-does-the-slash-in-the-parameter-list-of-a-function-mean" id="id26">函数形参列表中的斜杠(/)是什么意思?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#numbers-and-strings" id="id27">数字和字符串</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#how-do-i-specify-hexadecimal-and-octal-integers" id="id28">如何给出十六进制和八进制整数?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-does-22-10-return-3" id="id29">为什么 -22 // 10 会返回 -3 ?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-convert-a-string-to-a-number" id="id30">如何将字符串转换为数字?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-convert-a-number-to-a-string" id="id31">如何将数字转换为字符串?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-modify-a-string-in-place" id="id32">如何修改字符串?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-use-strings-to-call-functions-methods" id="id33">如何使用字符串调用函数/方法?</a></p></li>
|
||
<li><p><a class="reference internal" href="#is-there-an-equivalent-to-perl-s-chomp-for-removing-trailing-newlines-from-strings" id="id34">是否有与Perl 的chomp() 等效的方法,用于从字符串中删除尾随换行符?</a></p></li>
|
||
<li><p><a class="reference internal" href="#is-there-a-scanf-or-sscanf-equivalent" id="id35">是否有 scanf() 或 sscanf() 的等价函数?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-does-unicodedecodeerror-or-unicodeencodeerror-error-mean" id="id36">'UnicodeDecodeError' 或 'UnicodeEncodeError' 错误是什么意思?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#performance" id="id37">性能</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#my-program-is-too-slow-how-do-i-speed-it-up" id="id38">我的程序太慢了。该如何加快速度?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-is-the-most-efficient-way-to-concatenate-many-strings-together" id="id39">将多个字符串连接在一起的最有效方法是什么?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#sequences-tuples-lists" id="id40">序列(元组/列表)</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#how-do-i-convert-between-tuples-and-lists" id="id41">如何在元组和列表之间进行转换?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-s-a-negative-index" id="id42">什么是负数索引?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-iterate-over-a-sequence-in-reverse-order" id="id43">序列如何以逆序遍历?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-you-remove-duplicates-from-a-list" id="id44">如何从列表中删除重复项?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-you-remove-multiple-items-from-a-list" id="id45">如何从列表中删除多个项?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-you-make-an-array-in-python" id="id46">如何在 Python 中创建数组?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-create-a-multidimensional-list" id="id47">如何创建多维列表?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-apply-a-method-to-a-sequence-of-objects" id="id48">如何将方法应用于一系列对象?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works" id="id49">为什么 a_tuple[i] += ['item'] 会引发异常?</a></p></li>
|
||
<li><p><a class="reference internal" href="#i-want-to-do-a-complicated-sort-can-you-do-a-schwartzian-transform-in-python" id="id50">我想做一个复杂的排序:能用 Python 进行施瓦茨变换吗?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-sort-one-list-by-values-from-another-list" id="id51">如何根据另一个列表的值对某列表进行排序?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#objects" id="id52">对象</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#what-is-a-class" id="id53">什么是类?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-is-a-method" id="id54">什么是方法?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-is-self" id="id55">什么是 self ?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-check-if-an-object-is-an-instance-of-a-given-class-or-of-a-subclass-of-it" id="id56">如何检查对象是否为给定类或其子类的一个实例?</a></p></li>
|
||
<li><p><a class="reference internal" href="#what-is-delegation" id="id57">什么是委托?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-call-a-method-defined-in-a-base-class-from-a-derived-class-that-overrides-it" id="id58">如何在派生类中调用被重载的基类方法?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-organize-my-code-to-make-it-easier-to-change-the-base-class" id="id59">如何让代码更容易对基类进行修改?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-create-static-class-data-and-static-class-methods" id="id60">如何创建静态类数据和静态类方法?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-overload-constructors-or-methods-in-python" id="id61">在 Python 中如何重载构造函数(或方法)?</a></p></li>
|
||
<li><p><a class="reference internal" href="#i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam" id="id62">在用 __spam 的时候得到一个类似 _SomeClassName__spam 的错误信息。</a></p></li>
|
||
<li><p><a class="reference internal" href="#my-class-defines-del-but-it-is-not-called-when-i-delete-the-object" id="id63">类定义了 __del__ 方法,但是删除对象时没有调用它。</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-get-a-list-of-all-instances-of-a-given-class" id="id64">如何获取给定类的所有实例的列表?</a></p></li>
|
||
<li><p><a class="reference internal" href="#why-does-the-result-of-id-appear-to-be-not-unique" id="id65">为什么 <code class="docutils literal notranslate"><span class="pre">id()</span></code> 的结果看起来不是唯一的?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
<li><p><a class="reference internal" href="#modules" id="id66">模块</a></p>
|
||
<ul>
|
||
<li><p><a class="reference internal" href="#how-do-i-create-a-pyc-file" id="id67">如何创建 .pyc 文件?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-do-i-find-the-current-module-name" id="id68">如何找到当前模块名称?</a></p></li>
|
||
<li><p><a class="reference internal" href="#how-can-i-have-modules-that-mutually-import-each-other" id="id69">如何让模块相互导入?</a></p></li>
|
||
<li><p><a class="reference internal" href="#import-x-y-z-returns-module-x-how-do-i-get-z" id="id70">__import__('x.y.z') 返回的是 <module 'x'> ;该如何得到 z 呢?</a></p></li>
|
||
<li><p><a class="reference internal" href="#when-i-edit-an-imported-module-and-reimport-it-the-changes-don-t-show-up-why-does-this-happen" id="id71">对已导入的模块进行了编辑并重新导入,但变动没有得以体现。这是为什么?</a></p></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<section id="general-questions">
|
||
<h2><a class="toc-backref" href="#id3">一般问题</a><a class="headerlink" href="#general-questions" title="永久链接至标题">¶</a></h2>
|
||
<section id="is-there-a-source-code-level-debugger-with-breakpoints-single-stepping-etc">
|
||
<h3><a class="toc-backref" href="#id4">Python 有没有提供带有断点、单步调试等功能的源码级调试器?</a><a class="headerlink" href="#is-there-a-source-code-level-debugger-with-breakpoints-single-stepping-etc" title="永久链接至标题">¶</a></h3>
|
||
<p>有的。</p>
|
||
<p>以下介绍了一些 Python 的调试器,用内置函数 <a class="reference internal" href="../library/functions.html#breakpoint" title="breakpoint"><code class="xref py py-func docutils literal notranslate"><span class="pre">breakpoint()</span></code></a> 即可切入这些调试器中。</p>
|
||
<p>pdb 模块是一个简单但是够用的控制台模式 Python 调试器。 它是标准 Python 库的一部分,并且 <a class="reference internal" href="../library/pdb.html#module-pdb" title="pdb: The Python debugger for interactive interpreters."><code class="xref py py-mod docutils literal notranslate"><span class="pre">已收录于库参考手册</span></code></a>。 你也可以通过使用 pdb 代码作为样例来编写你自己的调试器。</p>
|
||
<p>作为标准 Python 发行版附带组件的 IDLE 交互式环境(通常位于 Tools/scripts/idle)中包含一个图形化的调试器。</p>
|
||
<p>PythonWin 是一种 Python IDE,其中包含了一个基于 pdb 的 GUI 调试器。PythonWin 的调试器会为断点着色,并提供了相当多的超酷特性,例如调试非 PythonWin 程序等。PythonWin 是 <a class="reference external" href="https://github.com/mhammond/pywin32">pywin32</a> 项目的组成部分,也是 <a class="reference external" href="https://www.activestate.com/products/python/">ActivePython</a> 发行版的组成部分。</p>
|
||
<p><a class="reference external" href="http://eric-ide.python-projects.org/">Eric</a> 是一个基于PyQt和Scintilla编辑组件构建的IDE。</p>
|
||
<p><a class="reference external" href="https://github.com/rocky/python3-trepan/">trepan3k</a> 是一个类似 gdb 的调试器。</p>
|
||
<p><a class="reference external" href="https://code.visualstudio.com/">Visual Studio Code</a> 是包含了调试工具的 IDE,并集成了版本控制软件。</p>
|
||
<p>有许多商业 Python IDE 都包含了图形化调试器。包括:</p>
|
||
<ul class="simple">
|
||
<li><p><a class="reference external" href="https://wingware.com/">Wing IDE</a></p></li>
|
||
<li><p><a class="reference external" href="https://www.activestate.com/products/komodo-ide/">Komodo IDE</a></p></li>
|
||
<li><p><a class="reference external" href="https://www.jetbrains.com/pycharm/">PyCharm</a></p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="are-there-tools-to-help-find-bugs-or-perform-static-analysis">
|
||
<h3><a class="toc-backref" href="#id5">是否有能帮助寻找漏洞或执行静态分析的工具?</a><a class="headerlink" href="#are-there-tools-to-help-find-bugs-or-perform-static-analysis" title="永久链接至标题">¶</a></h3>
|
||
<p>有的。</p>
|
||
<p><a class="reference external" href="https://www.pylint.org/">Pylint</a> 和 <a class="reference external" href="https://github.com/PyCQA/pyflakes">Pyflakes</a> 可进行基本检查来帮助你尽早捕捉漏洞。</p>
|
||
<p>静态类型检查器,例如 <a class="reference external" href="http://mypy-lang.org/">Mypy</a> 、 <a class="reference external" href="https://pyre-check.org/">Pyre</a> 和 <a class="reference external" href="https://github.com/google/pytype">Pytype</a> 可以检查Python源代码中的类型提示。</p>
|
||
</section>
|
||
<section id="how-can-i-create-a-stand-alone-binary-from-a-python-script">
|
||
<h3><a class="toc-backref" href="#id6">如何由 Python 脚本创建能独立运行的二进制程序?</a><a class="headerlink" href="#how-can-i-create-a-stand-alone-binary-from-a-python-script" title="永久链接至标题">¶</a></h3>
|
||
<p>如果只是想要一个独立的程序,以便用户不必预先安装 Python 即可下载和运行它,则不需要将 Python 编译成 C 代码。有许多工具可以检测程序所需的模块,并将这些模块与 Python 二进制程序捆绑在一起生成单个可执行文件。</p>
|
||
<p>一种是使用冻结工具,它包含在Python源代码树 <code class="docutils literal notranslate"><span class="pre">Tools/freeze</span></code> 中。它将Python字节代码转换为C数组;一个C编译器,你可以将所有模块嵌入到一个新程序中,然后将其与标准Python模块链接。</p>
|
||
<p>它的工作原理是递归扫描源代码,获取两种格式的 import 语句,并在标准 Python 路径和源码目录(用于内置模块)检索这些模块。然后,把这些模块的 Python 字节码转换为 C 代码(可以利用 marshal 模块转换为代码对象的数组初始化器),并创建一个定制的配置文件,该文件仅包含程序实际用到的内置模块。然后,编译生成的 C 代码并将其与 Python 解释器的其余部分链接,形成一个自给自足的二进制文件,其功能与 Python 脚本代码完全相同。</p>
|
||
<p>显然,freeze 需要一个 C 编译器。 还有一些其他实用工具则不需要:</p>
|
||
<ul class="simple">
|
||
<li><p><a class="reference external" href="http://www.py2exe.org/">py2exe</a> 用于生成 Windows 版二进制可执行文件</p></li>
|
||
<li><p><a class="reference external" href="https://github.com/ronaldoussoren/py2app">py2app</a> 用于生成 Mac OS X 版二进制可执行文件</p></li>
|
||
<li><p><a class="reference external" href="https://cx-freeze.readthedocs.io/en/latest/">cx_Freeze</a> 用于生成跨平台的二进制可执行文件</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="are-there-coding-standards-or-a-style-guide-for-python-programs">
|
||
<h3><a class="toc-backref" href="#id7">是否有 Python 编码标准或风格指南?</a><a class="headerlink" href="#are-there-coding-standards-or-a-style-guide-for-python-programs" title="永久链接至标题">¶</a></h3>
|
||
<p>有的。 标准库模块所要求的编码风格记录于 <span class="target" id="index-7"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a> 之中。</p>
|
||
</section>
|
||
</section>
|
||
<section id="core-language">
|
||
<h2><a class="toc-backref" href="#id8">语言核心内容</a><a class="headerlink" href="#core-language" title="永久链接至标题">¶</a></h2>
|
||
<section id="why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value">
|
||
<h3><a class="toc-backref" href="#id9">变量明明有值,为什么还会出现 UnboundLocalError?</a><a class="headerlink" href="#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value" title="永久链接至标题">¶</a></h3>
|
||
<p>因为在函数内部某处添加了一条赋值语句,导致之前正常工作的代码报出 UnboundLocalError 错误,这可能是有点令人惊讶。</p>
|
||
<p>以下代码:</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">bar</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">bar</span><span class="p">()</span>
|
||
<span class="go">10</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>正常工作,但是以下代码</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="gp">... </span> <span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>会得到一个 UnboundLocalError :</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">foo</span><span class="p">()</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
<span class="w"> </span><span class="c">...</span>
|
||
<span class="gr">UnboundLocalError</span>: <span class="n">local variable 'x' referenced before assignment</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>原因就是,当对某作用域内的变量进行赋值时,该变量将成为该作用域内的局部变量,并覆盖外部作用域中的同名变量。由于 foo 的最后一条语句为 <code class="docutils literal notranslate"><span class="pre">x</span></code> 分配了一个新值,编译器会将其识别为局部变量。因此,前面的 <code class="docutils literal notranslate"><span class="pre">print(x)</span></code> 试图输出未初始化的局部变量,就会引发错误。</p>
|
||
<p>在上面的示例中,可以将外部作用域的变量声明为全局变量以便访问:</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">foobar</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="k">global</span> <span class="n">x</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="gp">... </span> <span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="gp">>>> </span><span class="n">foobar</span><span class="p">()</span>
|
||
<span class="go">10</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>与类和实例变量貌似但不一样,其实以上是在修改外部作用域的变量值,为了提示这一点,这里需要显式声明一下。</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="go">11</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>你可以使用 <a class="reference internal" href="../reference/simple_stmts.html#nonlocal"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">nonlocal</span></code></a> 关键字在嵌套作用域中执行类似的操作:</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
|
||
<span class="gp">... </span> <span class="k">def</span> <span class="nf">bar</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="k">nonlocal</span> <span class="n">x</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="gp">... </span> <span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="gp">... </span> <span class="n">bar</span><span class="p">()</span>
|
||
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">foo</span><span class="p">()</span>
|
||
<span class="go">10</span>
|
||
<span class="go">11</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="what-are-the-rules-for-local-and-global-variables-in-python">
|
||
<h3><a class="toc-backref" href="#id10">Python 的局部变量和全局变量有哪些规则?</a><a class="headerlink" href="#what-are-the-rules-for-local-and-global-variables-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>函数内部只作引用的 Python 变量隐式视为全局变量。如果在函数内部任何位置为变量赋值,则除非明确声明为全局变量,否则均将其视为局部变量。</p>
|
||
<p>起初尽管有点令人惊讶,不过考虑片刻即可释然。一方面,已分配的变量要求加上 <a class="reference internal" href="../reference/simple_stmts.html#global"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">global</span></code></a> 可以防止意外的副作用发生。另一方面,如果所有全局引用都要加上 <code class="docutils literal notranslate"><span class="pre">global</span></code> ,那处处都得用上 <code class="docutils literal notranslate"><span class="pre">global</span></code> 了。那么每次对内置函数或导入模块中的组件进行引用时,都得声明为全局变量。这种杂乱会破坏 <code class="docutils literal notranslate"><span class="pre">global</span></code> 声明用于警示副作用的有效性。</p>
|
||
</section>
|
||
<section id="why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result">
|
||
<h3><a class="toc-backref" href="#id11">为什么在循环中定义的参数各异的 lambda 都返回相同的结果?</a><a class="headerlink" href="#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result" title="永久链接至标题">¶</a></h3>
|
||
<p>假设用 for 循环来定义几个取值各异的 lambda(即便是普通函数也一样):</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">squares</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="gp">>>> </span><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">squares</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>以上会得到一个包含5个 lambda 函数的列表,这些函数将计算 <code class="docutils literal notranslate"><span class="pre">x**2</span></code>。大家或许期望,调用这些函数会分别返回 <code class="docutils literal notranslate"><span class="pre">0</span></code> 、<code class="docutils literal notranslate"><span class="pre">1</span></code> 、 <code class="docutils literal notranslate"><span class="pre">4</span></code> 、 <code class="docutils literal notranslate"><span class="pre">9</span></code> 和 <code class="docutils literal notranslate"><span class="pre">16</span></code>。然而,真的试过就会发现,他们都会返回 <code class="docutils literal notranslate"><span class="pre">16</span></code> :</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">squares</span><span class="p">[</span><span class="mi">2</span><span class="p">]()</span>
|
||
<span class="go">16</span>
|
||
<span class="gp">>>> </span><span class="n">squares</span><span class="p">[</span><span class="mi">4</span><span class="p">]()</span>
|
||
<span class="go">16</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这是因为 <code class="docutils literal notranslate"><span class="pre">x</span></code> 不是 lambda 函数的内部变量,而是定义于外部作用域中的,并且 <code class="docutils literal notranslate"><span class="pre">x</span></code> 是在调用 lambda 时访问的——而不是在定义时访问。循环结束时 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的值是 <code class="docutils literal notranslate"><span class="pre">4</span></code> ,所以此时所有的函数都将返回 <code class="docutils literal notranslate"><span class="pre">4**2</span></code> ,即 <code class="docutils literal notranslate"><span class="pre">16</span></code> 。通过改变 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的值并查看 lambda 的结果变化,也可以验证这一点。</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">8</span>
|
||
<span class="gp">>>> </span><span class="n">squares</span><span class="p">[</span><span class="mi">2</span><span class="p">]()</span>
|
||
<span class="go">64</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>为了避免发生上述情况,需要将值保存在 lambda 局部变量,以使其不依赖于全局 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的值:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">squares</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="gp">>>> </span><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">squares</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="k">lambda</span> <span class="n">n</span><span class="o">=</span><span class="n">x</span><span class="p">:</span> <span class="n">n</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>以上 <code class="docutils literal notranslate"><span class="pre">n=x</span></code> 创建了一个新的 lambda 本地变量 <code class="docutils literal notranslate"><span class="pre">n</span></code>,并在定义 lambda 时计算其值,使其与循环当前时点的 <code class="docutils literal notranslate"><span class="pre">x</span></code> 值相同。这意味着 <code class="docutils literal notranslate"><span class="pre">n</span></code> 的值在第 1 个 lambda 中为 <code class="docutils literal notranslate"><span class="pre">0</span></code> ,在第 2 个 lambda 中为 <code class="docutils literal notranslate"><span class="pre">1</span></code> ,在第 3 个中为 <code class="docutils literal notranslate"><span class="pre">2</span></code>,依此类推。因此现在每个 lambda 都会返回正确结果:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">squares</span><span class="p">[</span><span class="mi">2</span><span class="p">]()</span>
|
||
<span class="go">4</span>
|
||
<span class="gp">>>> </span><span class="n">squares</span><span class="p">[</span><span class="mi">4</span><span class="p">]()</span>
|
||
<span class="go">16</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>请注意,上述表现并不是 lambda 所特有的,常规的函数也同样适用。</p>
|
||
</section>
|
||
<section id="how-do-i-share-global-variables-across-modules">
|
||
<h3><a class="toc-backref" href="#id12">如何跨模块共享全局变量?</a><a class="headerlink" href="#how-do-i-share-global-variables-across-modules" title="永久链接至标题">¶</a></h3>
|
||
<p>在单个程序中跨模块共享信息的规范方法是创建一个特殊模块(通常称为 config 或 cfg)。只需在应用程序的所有模块中导入该 config 模块;然后该模块就可当作全局名称使用了。因为每个模块只有一个实例,所以对该模块对象所做的任何更改将会在所有地方得以体现。 例如:</p>
|
||
<p>config.py:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># Default value of the 'x' configuration setting</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>mod.py:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">config</span>
|
||
<span class="n">config</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>main.py:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">config</span>
|
||
<span class="kn">import</span> <span class="nn">mod</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">x</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>请注意,出于同样的原因,采用模块也是实现单例设计模式的基础。</p>
|
||
</section>
|
||
<section id="what-are-the-best-practices-for-using-import-in-a-module">
|
||
<h3><a class="toc-backref" href="#id13">导入模块的“最佳实践”是什么?</a><a class="headerlink" href="#what-are-the-best-practices-for-using-import-in-a-module" title="永久链接至标题">¶</a></h3>
|
||
<p>通常请勿使用 <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">modulename</span> <span class="pre">import</span> <span class="pre">*</span></code> 。因为这会扰乱 importer 的命名空间,且会造成未定义名称更难以被 Linter 检查出来。</p>
|
||
<p>请在代码文件的首部就导入模块。这样代码所需的模块就一目了然了,也不用考虑模块名是否在作用域内的问题。每行导入一个模块则增删起来会比较容易,每行导入多个模块则更节省屏幕空间。</p>
|
||
<p>按如下顺序导入模块就是一种好做法:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>标准库模块——比如: <code class="docutils literal notranslate"><span class="pre">sys</span></code> 、 <code class="docutils literal notranslate"><span class="pre">os</span></code> 、 <code class="docutils literal notranslate"><span class="pre">getopt</span></code> 、 <code class="docutils literal notranslate"><span class="pre">re</span></code> 等。</p></li>
|
||
<li><p>第三方库模块(安装于 Python site-packages 目录中的内容)——如 mx.DateTime、ZODB、PIL.Image 等。</p></li>
|
||
<li><p>本地开发的模块</p></li>
|
||
</ol>
|
||
<p>为了避免循环导入引发的问题,有时需要将模块导入语句移入函数或类的内部。Gordon McMillan 的说法如下:</p>
|
||
<blockquote>
|
||
<div><p>当两个模块都采用 "import <module>" 的导入形式时,循环导入是没有问题的。但如果第 2 个模块想从第 1 个模块中取出一个名称("from module import name")并且导入处于代码的最顶层,那导入就会失败。原因是第 1 个模块中的名称还不可用,这时第 1 个模块正忙于导入第 2 个模块呢。</p>
|
||
</div></blockquote>
|
||
<p>如果只是在一个函数中用到第 2 个模块,那这时将导入语句移入该函数内部即可。当调用到导入语句时,第 1 个模块将已经完成初始化,第 2 个模块就可以进行导入了。</p>
|
||
<p>如果某些模块是平台相关的,可能还需要把导入语句移出最顶级代码。这种情况下,甚至有可能无法导入文件首部的所有模块。于是在对应的平台相关代码中导入正确的模块,就是一种不错的选择。</p>
|
||
<p>只有为了避免循环导入问题,或有必要减少模块初始化时间时,才把导入语句移入类似函数定义内部的局部作用域。如果根据程序的执行方式,许多导入操作不是必需的,那么这种技术尤其有用。如果模块仅在某个函数中用到,可能还要将导入操作移入该函数内部。请注意,因为模块有一次初始化过程,所以第一次加载模块的代价可能会比较高,但多次加载几乎没有什么花费,代价只是进行几次字典检索而已。即使模块名超出了作用域,模块在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中也是可用的。</p>
|
||
</section>
|
||
<section id="why-are-default-values-shared-between-objects">
|
||
<h3><a class="toc-backref" href="#id14">为什么对象之间会共享默认值?</a><a class="headerlink" href="#why-are-default-values-shared-between-objects" title="永久链接至标题">¶</a></h3>
|
||
<p>新手程序员常常中招这类 Bug。请看以下函数:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="p">{}):</span> <span class="c1"># Danger: shared reference to one dict for all calls</span>
|
||
<span class="o">...</span> <span class="n">compute</span> <span class="n">something</span> <span class="o">...</span>
|
||
<span class="n">mydict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="k">return</span> <span class="n">mydict</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>第一次调用此函数时, <code class="docutils literal notranslate"><span class="pre">mydict</span></code> 中只有一个数据项。第二次调用 <code class="docutils literal notranslate"><span class="pre">mydict</span></code> 则会包含两个数据项,因为 <code class="docutils literal notranslate"><span class="pre">foo()</span></code> 开始执行时, <code class="docutils literal notranslate"><span class="pre">mydict</span></code> 中已经带有一个数据项了。</p>
|
||
<p>大家往往希望,函数调用会为默认值创建新的对象。但事实并非如此。默认值只会在函数定义时创建一次。如果对象发生改变,就如上例中的字典那样,则后续调用该函数时将会引用这个改动的对象。</p>
|
||
<p>按照定义,不可变对象改动起来是安全的,诸如数字、字符串、元组和 <code class="docutils literal notranslate"><span class="pre">None</span></code> 之类。而可变对象的改动则可能引起困惑,例如字典、列表和类实例等。</p>
|
||
<p>因此,不把可变对象用作默认值是一种良好的编程做法。而应采用 <code class="docutils literal notranslate"><span class="pre">None</span></code> 作为默认值,然后在函数中检查参数是否为 <code class="docutils literal notranslate"><span class="pre">None</span></code> 并新建列表、字典或其他对象。例如,代码不应如下所示:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="p">{}):</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>而应这么写:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">mydict</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">mydict</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">mydict</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># create a new dict for local namespace</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>参数默认值的特性有时会很有用处。 如果有个函数的计算过程会比较耗时,有一种常见技巧是将每次函数调用的参数和结果缓存起来,并在同样的值被再次请求时返回缓存的值。这种技巧被称为“memoize”,实现代码可如下所示:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># Callers can only provide two parameters and optionally pass _cache by keyword</span>
|
||
<span class="k">def</span> <span class="nf">expensive</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">_cache</span><span class="o">=</span><span class="p">{}):</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)</span> <span class="ow">in</span> <span class="n">_cache</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">_cache</span><span class="p">[(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)]</span>
|
||
|
||
<span class="c1"># Calculate the value</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="o">...</span> <span class="n">expensive</span> <span class="n">computation</span> <span class="o">...</span>
|
||
<span class="n">_cache</span><span class="p">[(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">)]</span> <span class="o">=</span> <span class="n">result</span> <span class="c1"># Store result in the cache</span>
|
||
<span class="k">return</span> <span class="n">result</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>也可以不用参数默认值来实现,而是采用全局的字典变量;这取决于个人偏好。</p>
|
||
</section>
|
||
<section id="how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another">
|
||
<h3><a class="toc-backref" href="#id15">如何将可选参数或关键字参数从一个函数传递到另一个函数?</a><a class="headerlink" href="#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another" title="永久链接至标题">¶</a></h3>
|
||
<p>请利用函数参数列表中的标识符 <code class="docutils literal notranslate"><span class="pre">*</span></code> 和 <code class="docutils literal notranslate"><span class="pre">**</span></code> 归集实参;结果会是元组形式的位置实参和字典形式的关键字实参。然后就可利用 <code class="docutils literal notranslate"><span class="pre">*</span></code> 和 <code class="docutils literal notranslate"><span class="pre">**</span></code> 在调用其他函数时传入这些实参:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="n">kwargs</span><span class="p">[</span><span class="s1">'width'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'14.3c'</span>
|
||
<span class="o">...</span>
|
||
<span class="n">g</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="what-is-the-difference-between-arguments-and-parameters">
|
||
<span id="faq-argument-vs-parameter"></span><span id="index-1"></span><h3><a class="toc-backref" href="#id16">形参和实参之间有什么区别?</a><a class="headerlink" href="#what-is-the-difference-between-arguments-and-parameters" title="永久链接至标题">¶</a></h3>
|
||
<p><a class="reference internal" href="../glossary.html#term-parameter"><span class="xref std std-term">形参</span></a> 是指出现在函数定义中的名称,而 <a class="reference internal" href="../glossary.html#term-argument"><span class="xref std std-term">实参</span></a> 则是在调用函数时实际传入的值。 形参定义了一个函数能接受何种类型的实参。 例如,对于以下函数定义:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><em>foo</em> 、 <em>bar</em> 和 <em>kwargs</em> 是 <code class="docutils literal notranslate"><span class="pre">func</span></code> 的形参。 不过在调用 <code class="docutils literal notranslate"><span class="pre">func</span></code> 时,例如:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">func</span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="n">bar</span><span class="o">=</span><span class="mi">314</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="n">somevar</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">42</span></code> 、 <code class="docutils literal notranslate"><span class="pre">314</span></code> 和 <code class="docutils literal notranslate"><span class="pre">somevar</span></code> 则是实参。</p>
|
||
</section>
|
||
<section id="why-did-changing-list-y-also-change-list-x">
|
||
<h3><a class="toc-backref" href="#id17">为什么修改列表 'y' 也会更改列表 'x'?</a><a class="headerlink" href="#why-did-changing-list-y-also-change-list-x" title="永久链接至标题">¶</a></h3>
|
||
<p>如果代码编写如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="gp">>>> </span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span>
|
||
<span class="gp">>>> </span><span class="n">y</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">y</span>
|
||
<span class="go">[10]</span>
|
||
<span class="gp">>>> </span><span class="n">x</span>
|
||
<span class="go">[10]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>或许大家很想知道,为什么在 y 中添加一个元素时, x 也会改变。</p>
|
||
<p>产生这种结果有两个因素:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>变量只是指向对象的一个名称。执行 <code class="docutils literal notranslate"><span class="pre">y</span> <span class="pre">=</span> <span class="pre">x</span></code> 并不会创建列表的副本——而只是创建了一个新变量 <code class="docutils literal notranslate"><span class="pre">y</span></code>,并指向 <code class="docutils literal notranslate"><span class="pre">x</span></code> 所指的同一对象。这就意味着只存在一个列表对象,<code class="docutils literal notranslate"><span class="pre">x</span></code> 和 <code class="docutils literal notranslate"><span class="pre">y</span></code> 都是对它的引用。</p></li>
|
||
<li><p>列表属于 <a class="reference internal" href="../glossary.html#term-mutable"><span class="xref std std-term">mutable</span></a> 对象,这意味着它的内容是可以修改的。</p></li>
|
||
</ol>
|
||
<p>在调用 <code class="xref py py-meth docutils literal notranslate"><span class="pre">append()</span></code> 之后,该可变对象的内容由 <code class="docutils literal notranslate"><span class="pre">[]</span></code> 变为 <code class="docutils literal notranslate"><span class="pre">[10]</span></code>。由于 x 和 y 这两个变量引用了同一对象,因此用其中任意一个名称所访问到的都是修改后的值 <code class="docutils literal notranslate"><span class="pre">[10]</span></code>。</p>
|
||
<p>如果把赋给 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的对象换成一个不可变对象:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># ints are immutable</span>
|
||
<span class="gp">>>> </span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span>
|
||
<span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1"># 5 can't be mutated, we are creating a new object here</span>
|
||
<span class="gp">>>> </span><span class="n">x</span>
|
||
<span class="go">6</span>
|
||
<span class="gp">>>> </span><span class="n">y</span>
|
||
<span class="go">5</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>可见这时 <code class="docutils literal notranslate"><span class="pre">x</span></code> 和 <code class="docutils literal notranslate"><span class="pre">y</span></code> 就不再相等了。因为整数是 <a class="reference internal" href="../glossary.html#term-immutable"><span class="xref std std-term">immutable</span></a> 对象,在执行 <code class="docutils literal notranslate"><span class="pre">x</span> <span class="pre">=</span> <span class="pre">x</span> <span class="pre">+</span> <span class="pre">1</span></code> 时,并不会修改整数对象 <code class="docutils literal notranslate"><span class="pre">5</span></code>,给它加上 1;而是创建了一个新的对象(整数对象 <code class="docutils literal notranslate"><span class="pre">6</span></code> )并将其赋给 <code class="docutils literal notranslate"><span class="pre">x</span></code> (也就是改变了 <code class="docutils literal notranslate"><span class="pre">x</span></code> 所指向的对象)。在赋值完成后,就有了两个对象(整数对象 <code class="docutils literal notranslate"><span class="pre">6</span></code> 和 <code class="docutils literal notranslate"><span class="pre">5</span></code> )和分别指向他俩的两个变量( <code class="docutils literal notranslate"><span class="pre">x</span></code> 现在指向 <code class="docutils literal notranslate"><span class="pre">6</span></code> 而 <code class="docutils literal notranslate"><span class="pre">y</span></code> 仍然指向 <code class="docutils literal notranslate"><span class="pre">5</span></code> )。</p>
|
||
<p>某些操作(例如 <code class="docutils literal notranslate"><span class="pre">y.append(10)</span></code> 和 <code class="docutils literal notranslate"><span class="pre">y.sort()</span></code> )会直接修改原对象,而看上去相似的另一些操作(例如 <code class="docutils literal notranslate"><span class="pre">y</span> <span class="pre">=</span> <span class="pre">y</span> <span class="pre">+</span> <span class="pre">[10]</span></code> 和 <code class="docutils literal notranslate"><span class="pre">sorted(y)</span></code> )则会创建新的对象。通常在 Python 中(以及所有标准库),直接修改原对象的方法将会返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> ,以助避免混淆这两种不同类型的操作。因此如果误用了 <code class="docutils literal notranslate"><span class="pre">y.sort()</span></code> 并期望返回 <code class="docutils literal notranslate"><span class="pre">y</span></code> 的有序副本,则结果只会是 <code class="docutils literal notranslate"><span class="pre">None</span></code> ,这可能就能让程序引发一条容易诊断的错误。</p>
|
||
<p>不过还存在一类操作,用不同的类型执行相同的操作有时会发生不同的行为:即增量赋值运算符。例如,<code class="docutils literal notranslate"><span class="pre">+=</span></code> 会修改列表,但不会修改元组或整数(<code class="docutils literal notranslate"><span class="pre">a_list</span> <span class="pre">+=</span> <span class="pre">[1,</span> <span class="pre">2,</span> <span class="pre">3]</span></code> 与 <code class="docutils literal notranslate"><span class="pre">a_list.extend([1,</span> <span class="pre">2,</span> <span class="pre">3])</span></code> 同样都会改变 <code class="docutils literal notranslate"><span class="pre">a_list</span></code>,而 <code class="docutils literal notranslate"><span class="pre">some_tuple</span> <span class="pre">+=</span> <span class="pre">(1,</span> <span class="pre">2,</span> <span class="pre">3)</span></code> 和 <code class="docutils literal notranslate"><span class="pre">some_int</span> <span class="pre">+=</span> <span class="pre">1</span></code> 则会创建新的对象)。</p>
|
||
<p>换而言之:</p>
|
||
<ul class="simple">
|
||
<li><p>对于一个可变对象( <a class="reference internal" href="../library/stdtypes.html#list" title="list"><code class="xref py py-class docutils literal notranslate"><span class="pre">list</span></code></a> 、 <a class="reference internal" href="../library/stdtypes.html#dict" title="dict"><code class="xref py py-class docutils literal notranslate"><span class="pre">dict</span></code></a> 、 <a class="reference internal" href="../library/stdtypes.html#set" title="set"><code class="xref py py-class docutils literal notranslate"><span class="pre">set</span></code></a> 等等),可以利用某些特定的操作进行修改,所有引用它的变量都会反映出改动情况。</p></li>
|
||
<li><p>对于一个不可变对象( <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a> 、 <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a> 、 <a class="reference internal" href="../library/stdtypes.html#tuple" title="tuple"><code class="xref py py-class docutils literal notranslate"><span class="pre">tuple</span></code></a> 等),所有引用它的变量都会给出相同的值,但所有改变其值的操作都将返回一个新的对象。</p></li>
|
||
</ul>
|
||
<p>如要知道两个变量是否指向同一个对象,可以利用 <a class="reference internal" href="../reference/expressions.html#is"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">is</span></code></a> 运算符或内置函数 <a class="reference internal" href="../library/functions.html#id" title="id"><code class="xref py py-func docutils literal notranslate"><span class="pre">id()</span></code></a>。</p>
|
||
</section>
|
||
<section id="how-do-i-write-a-function-with-output-parameters-call-by-reference">
|
||
<h3><a class="toc-backref" href="#id18">如何编写带有输出参数的函数(按照引用调用)?</a><a class="headerlink" href="#how-do-i-write-a-function-with-output-parameters-call-by-reference" title="永久链接至标题">¶</a></h3>
|
||
<p>请记住,Python 中的实参是通过赋值传递的。由于赋值只是创建了对象的引用,所以调用方和被调用方的参数名都不存在别名,本质上也就不存在按引用调用的方式。通过以下几种方式,可以得到所需的效果。</p>
|
||
<ol class="arabic">
|
||
<li><p>返回一个元组:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">def</span> <span class="nf">func1</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">a</span> <span class="o">=</span> <span class="s1">'new-value'</span> <span class="c1"># a and b are local names</span>
|
||
<span class="gp">... </span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1"># assigned to new objects</span>
|
||
<span class="gp">... </span> <span class="k">return</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="c1"># return new values</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s1">'old-value'</span><span class="p">,</span> <span class="mi">99</span>
|
||
<span class="gp">>>> </span><span class="n">func1</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
|
||
<span class="go">('new-value', 100)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这差不多是最明晰的解决方案了。</p>
|
||
</li>
|
||
<li><p>使用全局变量。这不是线程安全的方案,不推荐使用。</p></li>
|
||
<li><p>传递一个可变(即可原地修改的) 对象:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">def</span> <span class="nf">func2</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'new-value'</span> <span class="c1"># 'a' references a mutable list</span>
|
||
<span class="gp">... </span> <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1"># changes a shared object</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'old-value'</span><span class="p">,</span> <span class="mi">99</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">func2</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">args</span>
|
||
<span class="go">['new-value', 100]</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><p>传入一个接收可变对象的字典:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">def</span> <span class="nf">func3</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">args</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'new-value'</span> <span class="c1"># args is a mutable dictionary</span>
|
||
<span class="gp">... </span> <span class="n">args</span><span class="p">[</span><span class="s1">'b'</span><span class="p">]</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="s1">'b'</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1"># change it in-place</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="n">args</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'a'</span><span class="p">:</span> <span class="s1">'old-value'</span><span class="p">,</span> <span class="s1">'b'</span><span class="p">:</span> <span class="mi">99</span><span class="p">}</span>
|
||
<span class="gp">>>> </span><span class="n">func3</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">args</span>
|
||
<span class="go">{'a': 'new-value', 'b': 100}</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><p>或者把值用类实例封装起来:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">class</span> <span class="nc">Namespace</span><span class="p">:</span>
|
||
<span class="gp">... </span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">/</span><span class="p">,</span> <span class="o">**</span><span class="n">args</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">args</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
<span class="gp">... </span> <span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">func4</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="n">args</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="s1">'new-value'</span> <span class="c1"># args is a mutable Namespace</span>
|
||
<span class="gp">... </span> <span class="n">args</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">b</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1"># change object in-place</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="n">args</span> <span class="o">=</span> <span class="n">Namespace</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="s1">'old-value'</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">99</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">func4</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="go">{'a': 'new-value', 'b': 100}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>没有什么理由要把问题搞得这么复杂。</p>
|
||
</li>
|
||
</ol>
|
||
<p>最佳选择就是返回一个包含多个结果值的元组。</p>
|
||
</section>
|
||
<section id="how-do-you-make-a-higher-order-function-in-python">
|
||
<h3><a class="toc-backref" href="#id19">如何在 Python 中创建高阶函数?</a><a class="headerlink" href="#how-do-you-make-a-higher-order-function-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>有两种选择:嵌套作用域、可调用对象。假定需要定义 <code class="docutils literal notranslate"><span class="pre">linear(a,b)</span></code> ,其返回结果是一个计算出 <code class="docutils literal notranslate"><span class="pre">a*x+b</span></code> 的函数 <code class="docutils literal notranslate"><span class="pre">f(x)</span></code>。 采用嵌套作用域的方案如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">linear</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">result</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">a</span> <span class="o">*</span> <span class="n">x</span> <span class="o">+</span> <span class="n">b</span>
|
||
<span class="k">return</span> <span class="n">result</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>或者可采用可调用对象:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">linear</span><span class="p">:</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">*</span> <span class="n">x</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>采用这两种方案时:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">taxes</span> <span class="o">=</span> <span class="n">linear</span><span class="p">(</span><span class="mf">0.3</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>都会得到一个可调用对象,可实现 <code class="docutils literal notranslate"><span class="pre">taxes(10e6)</span> <span class="pre">==</span> <span class="pre">0.3</span> <span class="pre">*</span> <span class="pre">10e6</span> <span class="pre">+</span> <span class="pre">2</span></code> 。</p>
|
||
<p>可调用对象的方案有个缺点,就是速度稍慢且生成的代码略长。不过值得注意的是,同一组可调用对象能够通过继承来共享签名(类声明):</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">exponential</span><span class="p">(</span><span class="n">linear</span><span class="p">):</span>
|
||
<span class="c1"># __init__ inherited</span>
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">*</span> <span class="p">(</span><span class="n">x</span> <span class="o">**</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>对象可以为多个方法的运行状态进行封装:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">counter</span><span class="p">:</span>
|
||
|
||
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="k">def</span> <span class="nf">set</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">x</span>
|
||
|
||
<span class="k">def</span> <span class="nf">up</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">+</span> <span class="mi">1</span>
|
||
|
||
<span class="k">def</span> <span class="nf">down</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="mi">1</span>
|
||
|
||
<span class="n">count</span> <span class="o">=</span> <span class="n">counter</span><span class="p">()</span>
|
||
<span class="n">inc</span><span class="p">,</span> <span class="n">dec</span><span class="p">,</span> <span class="n">reset</span> <span class="o">=</span> <span class="n">count</span><span class="o">.</span><span class="n">up</span><span class="p">,</span> <span class="n">count</span><span class="o">.</span><span class="n">down</span><span class="p">,</span> <span class="n">count</span><span class="o">.</span><span class="n">set</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>以上 <code class="docutils literal notranslate"><span class="pre">inc()</span></code> 、 <code class="docutils literal notranslate"><span class="pre">dec()</span></code> 和 <code class="docutils literal notranslate"><span class="pre">reset()</span></code> 的表现,就如同共享了同一计数变量一样。</p>
|
||
</section>
|
||
<section id="how-do-i-copy-an-object-in-python">
|
||
<h3><a class="toc-backref" href="#id20">如何复制 Python 对象?</a><a class="headerlink" href="#how-do-i-copy-an-object-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>一般情况下,用 <a class="reference internal" href="../library/copy.html#copy.copy" title="copy.copy"><code class="xref py py-func docutils literal notranslate"><span class="pre">copy.copy()</span></code></a> 或 <a class="reference internal" href="../library/copy.html#copy.deepcopy" title="copy.deepcopy"><code class="xref py py-func docutils literal notranslate"><span class="pre">copy.deepcopy()</span></code></a> 基本就可以了。并不是所有对象都支持复制,但多数是可以的。</p>
|
||
<p>某些对象可以用更简便的方法进行复制。比如字典对象就提供了 <a class="reference internal" href="../library/stdtypes.html#dict.copy" title="dict.copy"><code class="xref py py-meth docutils literal notranslate"><span class="pre">copy()</span></code></a> 方法:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">newdict</span> <span class="o">=</span> <span class="n">olddict</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>序列可以用切片操作进行复制:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">new_l</span> <span class="o">=</span> <span class="n">l</span><span class="p">[:]</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-can-i-find-the-methods-or-attributes-of-an-object">
|
||
<h3><a class="toc-backref" href="#id21">如何找到对象的方法或属性?</a><a class="headerlink" href="#how-can-i-find-the-methods-or-attributes-of-an-object" title="永久链接至标题">¶</a></h3>
|
||
<p>假定 x 是一个用户自定义类的实例,<code class="docutils literal notranslate"><span class="pre">dir(x)</span></code> 将返回一个按字母排序的名称列表,其中包含了实例的属性及由类定义的方法和属性。</p>
|
||
</section>
|
||
<section id="how-can-my-code-discover-the-name-of-an-object">
|
||
<h3><a class="toc-backref" href="#id22">如何用代码获取对象的名称?</a><a class="headerlink" href="#how-can-my-code-discover-the-name-of-an-object" title="永久链接至标题">¶</a></h3>
|
||
<p>一般而言这是无法实现的,因为对象并不存在真正的名称。赋值本质上是把某个名称绑定到某个值上;<code class="docutils literal notranslate"><span class="pre">def</span></code> 和 <code class="docutils literal notranslate"><span class="pre">class</span></code> 语句同样如此,只是值换成了某个可调用对象。比如以下代码:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">class</span> <span class="nc">A</span><span class="p">:</span>
|
||
<span class="gp">... </span> <span class="k">pass</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="n">B</span> <span class="o">=</span> <span class="n">A</span>
|
||
<span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span>
|
||
<span class="gp">>>> </span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span>
|
||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
|
||
<span class="go"><__main__.A object at 0x16D07CC></span>
|
||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
|
||
<span class="go"><__main__.A object at 0x16D07CC></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>可以不太严谨地说,上述类具有一个名称:即便它绑定了两个名称并通过名称 B 发起调用,可是创建出来的实例仍被视为是类 A 的实例。但无法说出实例的名称是 a 还是 b,因为这两个名称都被绑定到同一个值上了。</p>
|
||
<p>代码一般没有必要去“知晓”某个值的名称。通常这种需求预示着还是改变方案为好,除非真的是要编写内审程序。</p>
|
||
<p>在 comp.lang.python 中,Fredrik Lundh 在回答这样的问题时曾经给出过一个绝佳的类比:</p>
|
||
<blockquote>
|
||
<div><p>这就像要知道家门口的那只猫的名字一样:猫(对象)自己不会说出它的名字,它根本就不在乎自己叫什么——所以唯一方法就是问一遍你所有的邻居(命名空间),这是不是他们家的猫(对象)……</p>
|
||
<p>……并且如果你发现它有很多名字或根本没有名字,那也不必惊讶!</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="what-s-up-with-the-comma-operator-s-precedence">
|
||
<h3><a class="toc-backref" href="#id23">逗号运算符的优先级是什么?</a><a class="headerlink" href="#what-s-up-with-the-comma-operator-s-precedence" title="永久链接至标题">¶</a></h3>
|
||
<p>逗号不是 Python 的运算符。 请看以下例子:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="s2">"a"</span> <span class="ow">in</span> <span class="s2">"b"</span><span class="p">,</span> <span class="s2">"a"</span>
|
||
<span class="go">(False, 'a')</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>由于逗号不是运算符,而只是表达式之间的分隔符,因此上述代码就相当于:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="s2">"a"</span> <span class="ow">in</span> <span class="s2">"b"</span><span class="p">),</span> <span class="s2">"a"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>而不是:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="s2">"a"</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"b"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>对于各种赋值运算符( <code class="docutils literal notranslate"><span class="pre">=</span></code> 、 <code class="docutils literal notranslate"><span class="pre">+=</span></code> 等)来说同样如此。他们并不是真正的运算符,而只是赋值语句中的语法分隔符。</p>
|
||
</section>
|
||
<section id="is-there-an-equivalent-of-c-s-ternary-operator">
|
||
<h3><a class="toc-backref" href="#id24">是否提供等价于 C 语言 "?:" 三目运算符的东西?</a><a class="headerlink" href="#is-there-an-equivalent-of-c-s-ternary-operator" title="永久链接至标题">¶</a></h3>
|
||
<p>有的。语法如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">on_true</span><span class="p">]</span> <span class="k">if</span> <span class="p">[</span><span class="n">expression</span><span class="p">]</span> <span class="k">else</span> <span class="p">[</span><span class="n">on_false</span><span class="p">]</span>
|
||
|
||
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">25</span>
|
||
<span class="n">small</span> <span class="o">=</span> <span class="n">x</span> <span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="n">y</span> <span class="k">else</span> <span class="n">y</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>在 Python 2.5 引入上述语法之前,通常的做法是使用逻辑运算符:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">expression</span><span class="p">]</span> <span class="ow">and</span> <span class="p">[</span><span class="n">on_true</span><span class="p">]</span> <span class="ow">or</span> <span class="p">[</span><span class="n">on_false</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>然而这种做法并不保险,因为当 <em>on_true</em> 为布尔值“假”时,结果将会出错。所以肯定还是采用 <code class="docutils literal notranslate"><span class="pre">...</span> <span class="pre">if</span> <span class="pre">...</span> <span class="pre">else</span> <span class="pre">...</span></code> 形式为妙。</p>
|
||
</section>
|
||
<section id="is-it-possible-to-write-obfuscated-one-liners-in-python">
|
||
<h3><a class="toc-backref" href="#id25">是否可以用 Python 编写让人眼晕的单行程序?</a><a class="headerlink" href="#is-it-possible-to-write-obfuscated-one-liners-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>可以。通常是在 <a class="reference internal" href="../reference/expressions.html#lambda"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">lambda</span></code></a> 中嵌套 <code class="xref std std-keyword docutils literal notranslate"><span class="pre">lambda</span></code> 来实现的。请参阅以下三个来自 Ulf Bartelt 的示例代码:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">reduce</span>
|
||
|
||
<span class="c1"># Primes < 1000</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">:</span><span class="n">y</span><span class="o">*</span><span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span><span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="o">!=</span><span class="mi">0</span><span class="p">,</span>
|
||
<span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">=</span><span class="n">y</span><span class="p">:</span><span class="n">y</span><span class="o">%</span><span class="n">x</span><span class="p">,</span><span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="nb">int</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="mf">0.5</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">))),</span><span class="mi">1</span><span class="p">),</span><span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">1000</span><span class="p">)))))</span>
|
||
|
||
<span class="c1"># First 10 Fibonacci numbers</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">f</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">f</span><span class="p">:(</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">f</span><span class="p">)</span><span class="o">+</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span><span class="n">f</span><span class="p">))</span> <span class="k">if</span> <span class="n">x</span><span class="o">></span><span class="mi">1</span> <span class="k">else</span> <span class="mi">1</span><span class="p">:</span>
|
||
<span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">f</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))))</span>
|
||
|
||
<span class="c1"># Mandelbrot set</span>
|
||
<span class="nb">print</span><span class="p">((</span><span class="k">lambda</span> <span class="n">Ru</span><span class="p">,</span><span class="n">Ro</span><span class="p">,</span><span class="n">Iu</span><span class="p">,</span><span class="n">Io</span><span class="p">,</span><span class="n">IM</span><span class="p">,</span><span class="n">Sx</span><span class="p">,</span><span class="n">Sy</span><span class="p">:</span><span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">,</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">,</span>
|
||
<span class="n">Iu</span><span class="o">=</span><span class="n">Iu</span><span class="p">,</span><span class="n">Io</span><span class="o">=</span><span class="n">Io</span><span class="p">,</span><span class="n">Ru</span><span class="o">=</span><span class="n">Ru</span><span class="p">,</span><span class="n">Ro</span><span class="o">=</span><span class="n">Ro</span><span class="p">,</span><span class="n">Sy</span><span class="o">=</span><span class="n">Sy</span><span class="p">,</span><span class="n">L</span><span class="o">=</span><span class="k">lambda</span> <span class="n">yc</span><span class="p">,</span><span class="n">Iu</span><span class="o">=</span><span class="n">Iu</span><span class="p">,</span><span class="n">Io</span><span class="o">=</span><span class="n">Io</span><span class="p">,</span><span class="n">Ru</span><span class="o">=</span><span class="n">Ru</span><span class="p">,</span><span class="n">Ro</span><span class="o">=</span><span class="n">Ro</span><span class="p">,</span><span class="n">i</span><span class="o">=</span><span class="n">IM</span><span class="p">,</span>
|
||
<span class="n">Sx</span><span class="o">=</span><span class="n">Sx</span><span class="p">,</span><span class="n">Sy</span><span class="o">=</span><span class="n">Sy</span><span class="p">:</span><span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">,</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">xc</span><span class="o">=</span><span class="n">Ru</span><span class="p">,</span><span class="n">yc</span><span class="o">=</span><span class="n">yc</span><span class="p">,</span><span class="n">Ru</span><span class="o">=</span><span class="n">Ru</span><span class="p">,</span><span class="n">Ro</span><span class="o">=</span><span class="n">Ro</span><span class="p">,</span>
|
||
<span class="n">i</span><span class="o">=</span><span class="n">i</span><span class="p">,</span><span class="n">Sx</span><span class="o">=</span><span class="n">Sx</span><span class="p">,</span><span class="n">F</span><span class="o">=</span><span class="k">lambda</span> <span class="n">xc</span><span class="p">,</span><span class="n">yc</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">f</span><span class="o">=</span><span class="k">lambda</span> <span class="n">xc</span><span class="p">,</span><span class="n">yc</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">f</span><span class="p">:(</span><span class="n">k</span><span class="o"><=</span><span class="mi">0</span><span class="p">)</span><span class="ow">or</span> <span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="n">y</span>
|
||
<span class="o">>=</span><span class="mf">4.0</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">1</span><span class="o">+</span><span class="n">f</span><span class="p">(</span><span class="n">xc</span><span class="p">,</span><span class="n">yc</span><span class="p">,</span><span class="n">x</span><span class="o">*</span><span class="n">x</span><span class="o">-</span><span class="n">y</span><span class="o">*</span><span class="n">y</span><span class="o">+</span><span class="n">xc</span><span class="p">,</span><span class="mf">2.0</span><span class="o">*</span><span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="o">+</span><span class="n">yc</span><span class="p">,</span><span class="n">k</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="n">f</span><span class="p">):</span><span class="n">f</span><span class="p">(</span><span class="n">xc</span><span class="p">,</span><span class="n">yc</span><span class="p">,</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">f</span><span class="p">):</span><span class="nb">chr</span><span class="p">(</span>
|
||
<span class="mi">64</span><span class="o">+</span><span class="n">F</span><span class="p">(</span><span class="n">Ru</span><span class="o">+</span><span class="n">x</span><span class="o">*</span><span class="p">(</span><span class="n">Ro</span><span class="o">-</span><span class="n">Ru</span><span class="p">)</span><span class="o">/</span><span class="n">Sx</span><span class="p">,</span><span class="n">yc</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">i</span><span class="p">)),</span><span class="nb">range</span><span class="p">(</span><span class="n">Sx</span><span class="p">))):</span><span class="n">L</span><span class="p">(</span><span class="n">Iu</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="p">(</span><span class="n">Io</span><span class="o">-</span><span class="n">Iu</span><span class="p">)</span><span class="o">/</span><span class="n">Sy</span><span class="p">),</span><span class="nb">range</span><span class="p">(</span><span class="n">Sy</span>
|
||
<span class="p">))))(</span><span class="o">-</span><span class="mf">2.1</span><span class="p">,</span> <span class="mf">0.7</span><span class="p">,</span> <span class="o">-</span><span class="mf">1.2</span><span class="p">,</span> <span class="mf">1.2</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">24</span><span class="p">))</span>
|
||
<span class="c1"># \___ ___/ \___ ___/ | | |__ lines on screen</span>
|
||
<span class="c1"># V V | |______ columns on screen</span>
|
||
<span class="c1"># | | |__________ maximum of "iterations"</span>
|
||
<span class="c1"># | |_________________ range on y axis</span>
|
||
<span class="c1"># |____________________________ range on x axis</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>请不要在家里尝试,骚年!</p>
|
||
</section>
|
||
<section id="what-does-the-slash-in-the-parameter-list-of-a-function-mean">
|
||
<span id="faq-positional-only-arguments"></span><h3><a class="toc-backref" href="#id26">函数形参列表中的斜杠(/)是什么意思?</a><a class="headerlink" href="#what-does-the-slash-in-the-parameter-list-of-a-function-mean" title="永久链接至标题">¶</a></h3>
|
||
<p>函数参数列表中的斜杠表示在它之前的形参全都仅限位置形参。仅限位置形参没有可供外部使用的名称。在调用仅接受位置形参的函数时,实参只会根据位置映射到形参上。假定 <a class="reference internal" href="../library/functions.html#divmod" title="divmod"><code class="xref py py-func docutils literal notranslate"><span class="pre">divmod()</span></code></a> 是一个仅接受位置形参的函数。 它的帮助文档如下所示:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">help</span><span class="p">(</span><span class="nb">divmod</span><span class="p">)</span>
|
||
<span class="go">Help on built-in function divmod in module builtins:</span>
|
||
|
||
<span class="go">divmod(x, y, /)</span>
|
||
<span class="go"> Return the tuple (x//y, x%y). Invariant: div*y + mod == x.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>形参列表尾部的斜杠说明,两个形参都是仅限位置形参。因此,用关键字参数调用 <a class="reference internal" href="../library/functions.html#divmod" title="divmod"><code class="xref py py-func docutils literal notranslate"><span class="pre">divmod()</span></code></a> 将会引发错误:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">divmod</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
|
||
<span class="gr">TypeError</span>: <span class="n">divmod() takes no keyword arguments</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="numbers-and-strings">
|
||
<h2><a class="toc-backref" href="#id27">数字和字符串</a><a class="headerlink" href="#numbers-and-strings" title="永久链接至标题">¶</a></h2>
|
||
<section id="how-do-i-specify-hexadecimal-and-octal-integers">
|
||
<h3><a class="toc-backref" href="#id28">如何给出十六进制和八进制整数?</a><a class="headerlink" href="#how-do-i-specify-hexadecimal-and-octal-integers" title="永久链接至标题">¶</a></h3>
|
||
<p>要给出八进制数,需在八进制数值前面加上一个零和一个小写或大写字母 "o" 作为前缀。例如,要将变量 "a" 设为八进制的 "10" (十进制的 8),写法如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="mo">0o10</span>
|
||
<span class="gp">>>> </span><span class="n">a</span>
|
||
<span class="go">8</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>十六进制数也很简单。只要在十六进制数前面加上一个零和一个小写或大写的字母 "x"。十六进制数中的字母可以为大写或小写。比如在 Python 解释器中输入:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="mh">0xa5</span>
|
||
<span class="gp">>>> </span><span class="n">a</span>
|
||
<span class="go">165</span>
|
||
<span class="gp">>>> </span><span class="n">b</span> <span class="o">=</span> <span class="mh">0XB2</span>
|
||
<span class="gp">>>> </span><span class="n">b</span>
|
||
<span class="go">178</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="why-does-22-10-return-3">
|
||
<h3><a class="toc-backref" href="#id29">为什么 -22 // 10 会返回 -3 ?</a><a class="headerlink" href="#why-does-22-10-return-3" title="永久链接至标题">¶</a></h3>
|
||
<p>这主要是为了让 <code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">%</span> <span class="pre">j</span></code> 的正负与 <code class="docutils literal notranslate"><span class="pre">j</span></code> 一致,如果期望如此,且期望如下等式成立:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">i</span> <span class="o">==</span> <span class="p">(</span><span class="n">i</span> <span class="o">//</span> <span class="n">j</span><span class="p">)</span> <span class="o">*</span> <span class="n">j</span> <span class="o">+</span> <span class="p">(</span><span class="n">i</span> <span class="o">%</span> <span class="n">j</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>那么整除就必须返回向下取整的结果。C 语言同样要求保持这种一致性,于是编译器在截断 <code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">//</span> <span class="pre">j</span></code> 的结果时需要让 <code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">%</span> <span class="pre">j</span></code> 的正负与 <code class="docutils literal notranslate"><span class="pre">i</span></code> 一致。</p>
|
||
<p>对于 <code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">%</span> <span class="pre">j</span></code> 来说 <code class="docutils literal notranslate"><span class="pre">j</span></code> 为负值的应用场景实际上是非常少的。 而 <code class="docutils literal notranslate"><span class="pre">j</span></code> 为正值的情况则非常多,并且实际上在所有情况下让 <code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">%</span> <span class="pre">j</span></code> 的结果为 <code class="docutils literal notranslate"><span class="pre">>=</span> <span class="pre">0</span></code> 会更有用处。 如果现在时间为 10 时,那么 200 小时前应是几时? <code class="docutils literal notranslate"><span class="pre">-190</span> <span class="pre">%</span> <span class="pre">12</span> <span class="pre">==</span> <span class="pre">2</span></code> 是有用处的;<code class="docutils literal notranslate"><span class="pre">-190</span> <span class="pre">%</span> <span class="pre">12</span> <span class="pre">==</span> <span class="pre">-10</span></code> 则是会导致意外的漏洞。</p>
|
||
</section>
|
||
<section id="how-do-i-convert-a-string-to-a-number">
|
||
<h3><a class="toc-backref" href="#id30">如何将字符串转换为数字?</a><a class="headerlink" href="#how-do-i-convert-a-string-to-a-number" title="永久链接至标题">¶</a></h3>
|
||
<p>对于整数,可使用内置的 <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-func docutils literal notranslate"><span class="pre">int()</span></code></a> 类型构造器,例如 <code class="docutils literal notranslate"><span class="pre">int('144')</span> <span class="pre">==</span> <span class="pre">144</span></code>。 类似地,可使用 <a class="reference internal" href="../library/functions.html#float" title="float"><code class="xref py py-func docutils literal notranslate"><span class="pre">float()</span></code></a> 转换为浮点数,例如 <code class="docutils literal notranslate"><span class="pre">float('144')</span> <span class="pre">==</span> <span class="pre">144.0</span></code>。</p>
|
||
<p>默认情况下,这些操作会将数字按十进制来解读,因此 <code class="docutils literal notranslate"><span class="pre">int('0144')</span> <span class="pre">==</span> <span class="pre">144</span></code> 而 <code class="docutils literal notranslate"><span class="pre">int('0x144')</span></code> 会引发 <a class="reference internal" href="../library/exceptions.html#ValueError" title="ValueError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ValueError</span></code></a>。 <code class="docutils literal notranslate"><span class="pre">int(string,</span> <span class="pre">base)</span></code> 接受第二个可选参数指定转换的基数,例如 <code class="docutils literal notranslate"><span class="pre">int('0x144',</span> <span class="pre">16)</span> <span class="pre">==</span> <span class="pre">324</span></code>。 如果指定基数为 0,则按 Python 规则解读数字:前缀 '0o' 表示八进制,而 '0x' 表示十六进制。</p>
|
||
<p>如果只是想把字符串转为数字,请不要使用内置函数 <a class="reference internal" href="../library/functions.html#eval" title="eval"><code class="xref py py-func docutils literal notranslate"><span class="pre">eval()</span></code></a>。 <a class="reference internal" href="../library/functions.html#eval" title="eval"><code class="xref py py-func docutils literal notranslate"><span class="pre">eval()</span></code></a> 的速度慢很多且存在安全风险:别人可能会传入带有不良副作用的 Python 表达式。比如可能会传入 <code class="docutils literal notranslate"><span class="pre">__import__('os').system("rm</span> <span class="pre">-rf</span> <span class="pre">$HOME")</span></code> ,这会把 home 目录给删了。</p>
|
||
<p><a class="reference internal" href="../library/functions.html#eval" title="eval"><code class="xref py py-func docutils literal notranslate"><span class="pre">eval()</span></code></a> 还有把数字解析为 Python 表达式的后果,因此如 <code class="docutils literal notranslate"><span class="pre">eval('09')</span></code> 将会导致语法错误,因为 Python 不允许十进制数带有前导 '0'('0' 除外)。</p>
|
||
</section>
|
||
<section id="how-do-i-convert-a-number-to-a-string">
|
||
<h3><a class="toc-backref" href="#id31">如何将数字转换为字符串?</a><a class="headerlink" href="#how-do-i-convert-a-number-to-a-string" title="永久链接至标题">¶</a></h3>
|
||
<p>比如要把数字 144 转换为字符串 '144',可使用内置类型构造器 <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-func docutils literal notranslate"><span class="pre">str()</span></code></a>。如果要表示为十六进制或八进制数格式,可使用内置函数 <a class="reference internal" href="../library/functions.html#hex" title="hex"><code class="xref py py-func docutils literal notranslate"><span class="pre">hex()</span></code></a> 或 <a class="reference internal" href="../library/functions.html#oct" title="oct"><code class="xref py py-func docutils literal notranslate"><span class="pre">oct()</span></code></a>。更复杂的格式化方法请参阅 <a class="reference internal" href="../reference/lexical_analysis.html#f-strings"><span class="std std-ref">格式字符串字面值</span></a> 和 <a class="reference internal" href="../library/string.html#formatstrings"><span class="std std-ref">格式字符串语法</span></a> 等章节,比如 <code class="docutils literal notranslate"><span class="pre">"{:04d}".format(144)</span></code> 会生成 <code class="docutils literal notranslate"><span class="pre">'0144'</span></code> , <code class="docutils literal notranslate"><span class="pre">"{:.3f}".format(1.0/3.0)</span></code> 则会生成 <code class="docutils literal notranslate"><span class="pre">'0.333'</span></code>。</p>
|
||
</section>
|
||
<section id="how-do-i-modify-a-string-in-place">
|
||
<h3><a class="toc-backref" href="#id32">如何修改字符串?</a><a class="headerlink" href="#how-do-i-modify-a-string-in-place" title="永久链接至标题">¶</a></h3>
|
||
<p>无法修改,因为字符串是不可变对象。 在大多数情况下,只要将各个部分组合起来构造出一个新字符串即可。如果需要一个能原地修改 Unicode 数据的对象,可以试试 <a class="reference internal" href="../library/io.html#io.StringIO" title="io.StringIO"><code class="xref py py-class docutils literal notranslate"><span class="pre">io.StringIO</span></code></a> 对象或 <a class="reference internal" href="../library/array.html#module-array" title="array: Space efficient arrays of uniformly typed numeric values."><code class="xref py py-mod docutils literal notranslate"><span class="pre">array</span></code></a> 模块:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">io</span>
|
||
<span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"Hello, world"</span>
|
||
<span class="gp">>>> </span><span class="n">sio</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">StringIO</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">sio</span><span class="o">.</span><span class="n">getvalue</span><span class="p">()</span>
|
||
<span class="go">'Hello, world'</span>
|
||
<span class="gp">>>> </span><span class="n">sio</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
|
||
<span class="go">7</span>
|
||
<span class="gp">>>> </span><span class="n">sio</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"there!"</span><span class="p">)</span>
|
||
<span class="go">6</span>
|
||
<span class="gp">>>> </span><span class="n">sio</span><span class="o">.</span><span class="n">getvalue</span><span class="p">()</span>
|
||
<span class="go">'Hello, there!'</span>
|
||
|
||
<span class="gp">>>> </span><span class="kn">import</span> <span class="nn">array</span>
|
||
<span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s1">'u'</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
|
||
<span class="go">array('u', 'Hello, world')</span>
|
||
<span class="gp">>>> </span><span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'y'</span>
|
||
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
|
||
<span class="go">array('u', 'yello, world')</span>
|
||
<span class="gp">>>> </span><span class="n">a</span><span class="o">.</span><span class="n">tounicode</span><span class="p">()</span>
|
||
<span class="go">'yello, world'</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-do-i-use-strings-to-call-functions-methods">
|
||
<h3><a class="toc-backref" href="#id33">如何使用字符串调用函数/方法?</a><a class="headerlink" href="#how-do-i-use-strings-to-call-functions-methods" title="永久链接至标题">¶</a></h3>
|
||
<p>有多种技巧可供选择。</p>
|
||
<ul>
|
||
<li><p>最好的做法是采用一个字典,将字符串映射为函数。其主要优势就是字符串不必与函数名一样。这也是用来模拟 case 结构的主要技巧:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">a</span><span class="p">():</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="k">def</span> <span class="nf">b</span><span class="p">():</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="n">dispatch</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'go'</span><span class="p">:</span> <span class="n">a</span><span class="p">,</span> <span class="s1">'stop'</span><span class="p">:</span> <span class="n">b</span><span class="p">}</span> <span class="c1"># Note lack of parens for funcs</span>
|
||
|
||
<span class="n">dispatch</span><span class="p">[</span><span class="n">get_input</span><span class="p">()]()</span> <span class="c1"># Note trailing parens to call function</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><p>利用内置函数 <a class="reference internal" href="../library/functions.html#getattr" title="getattr"><code class="xref py py-func docutils literal notranslate"><span class="pre">getattr()</span></code></a> :</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">foo</span>
|
||
<span class="nb">getattr</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="s1">'bar'</span><span class="p">)()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>请注意 <a class="reference internal" href="../library/functions.html#getattr" title="getattr"><code class="xref py py-func docutils literal notranslate"><span class="pre">getattr()</span></code></a> 可用于任何对象,包括类、类实例、模块等等。</p>
|
||
<p>标准库就多次使用了这个技巧,例如:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">do_foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">do_bar</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">foo_instance</span><span class="p">,</span> <span class="s1">'do_'</span> <span class="o">+</span> <span class="n">opname</span><span class="p">)</span>
|
||
<span class="n">f</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li><p>使用 <a class="reference internal" href="../library/functions.html#locals" title="locals"><code class="xref py py-func docutils literal notranslate"><span class="pre">locals()</span></code></a> 或 <a class="reference internal" href="../library/functions.html#eval" title="eval"><code class="xref py py-func docutils literal notranslate"><span class="pre">eval()</span></code></a> 来解析出函数名:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">myFunc</span><span class="p">():</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">)</span>
|
||
|
||
<span class="n">fname</span> <span class="o">=</span> <span class="s2">"myFunc"</span>
|
||
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">locals</span><span class="p">()[</span><span class="n">fname</span><span class="p">]</span>
|
||
<span class="n">f</span><span class="p">()</span>
|
||
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span>
|
||
<span class="n">f</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意:使用 <a class="reference internal" href="../library/functions.html#eval" title="eval"><code class="xref py py-func docutils literal notranslate"><span class="pre">eval()</span></code></a> 速度慢而且危险。 如果你不能绝对掌控字符串的内容,别人将能传入可被解析为任意函数直接执行的字符串。</p>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="is-there-an-equivalent-to-perl-s-chomp-for-removing-trailing-newlines-from-strings">
|
||
<h3><a class="toc-backref" href="#id34">是否有与Perl 的chomp() 等效的方法,用于从字符串中删除尾随换行符?</a><a class="headerlink" href="#is-there-an-equivalent-to-perl-s-chomp-for-removing-trailing-newlines-from-strings" title="永久链接至标题">¶</a></h3>
|
||
<p>可以使用 <code class="docutils literal notranslate"><span class="pre">S.rstrip("\r\n")</span></code> 从字符串 <code class="docutils literal notranslate"><span class="pre">S</span></code> 的末尾删除所有的换行符,而不删除其他尾随空格。如果字符串 <code class="docutils literal notranslate"><span class="pre">S</span></code> 表示多行,且末尾有几个空行,则将删除所有空行的换行符:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">lines</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"line 1 </span><span class="se">\r\n</span><span class="s2">"</span>
|
||
<span class="gp">... </span> <span class="s2">"</span><span class="se">\r\n</span><span class="s2">"</span>
|
||
<span class="gp">... </span> <span class="s2">"</span><span class="se">\r\n</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">lines</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\r</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="go">'line 1 '</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>由于通常只在一次读取一行文本时才需要这样做,所以使用 <code class="docutils literal notranslate"><span class="pre">S.rstrip()</span></code> 这种方式工作得很好。</p>
|
||
</section>
|
||
<section id="is-there-a-scanf-or-sscanf-equivalent">
|
||
<h3><a class="toc-backref" href="#id35">是否有 scanf() 或 sscanf() 的等价函数?</a><a class="headerlink" href="#is-there-a-scanf-or-sscanf-equivalent" title="永久链接至标题">¶</a></h3>
|
||
<p>没有。</p>
|
||
<p>如果要对简单的输入进行解析,最容易的做法通常是利用字符串对象的 <a class="reference internal" href="../library/stdtypes.html#str.split" title="str.split"><code class="xref py py-meth docutils literal notranslate"><span class="pre">split()</span></code></a> 方法将一行按空白符分隔拆分为多个单词,然后用 <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-func docutils literal notranslate"><span class="pre">int()</span></code></a> 或 <a class="reference internal" href="../library/functions.html#float" title="float"><code class="xref py py-func docutils literal notranslate"><span class="pre">float()</span></code></a> 将十进制数字符串转换为数字值。 <code class="docutils literal notranslate"><span class="pre">split()</span></code> 支持可选的 "sep" 形参,适用于分隔符不用空白符的情况。</p>
|
||
<p>如果要对更复杂的输入进行解析,那么正则表达式要比 C 语言的 <code class="xref c c-func docutils literal notranslate"><span class="pre">sscanf()</span></code> 更强大,也更合适。</p>
|
||
</section>
|
||
<section id="what-does-unicodedecodeerror-or-unicodeencodeerror-error-mean">
|
||
<h3><a class="toc-backref" href="#id36">'UnicodeDecodeError' 或 'UnicodeEncodeError' 错误是什么意思?</a><a class="headerlink" href="#what-does-unicodedecodeerror-or-unicodeencodeerror-error-mean" title="永久链接至标题">¶</a></h3>
|
||
<p>见 <a class="reference internal" href="../howto/unicode.html#unicode-howto"><span class="std std-ref">Unicode 指南</span></a></p>
|
||
</section>
|
||
</section>
|
||
<section id="performance">
|
||
<h2><a class="toc-backref" href="#id37">性能</a><a class="headerlink" href="#performance" title="永久链接至标题">¶</a></h2>
|
||
<section id="my-program-is-too-slow-how-do-i-speed-it-up">
|
||
<h3><a class="toc-backref" href="#id38">我的程序太慢了。该如何加快速度?</a><a class="headerlink" href="#my-program-is-too-slow-how-do-i-speed-it-up" title="永久链接至标题">¶</a></h3>
|
||
<p>总的来说,这是个棘手的问题。在进一步讨论之前,首先应该记住以下几件事:</p>
|
||
<ul class="simple">
|
||
<li><p>不同的 Python 实现具有不同的性能特点。 本 FAQ 着重解答的是 <a class="reference internal" href="../glossary.html#term-cpython"><span class="xref std std-term">CPython</span></a>。</p></li>
|
||
<li><p>不同操作系统可能会有不同表现,尤其是涉及 I/O 和多线程时。</p></li>
|
||
<li><p>在尝试优化代码 <em>之前</em> ,务必要先找出程序中的热点(请参阅 <a class="reference internal" href="../library/profile.html#module-profile" title="profile: Python source profiler."><code class="xref py py-mod docutils literal notranslate"><span class="pre">profile</span></code></a> 模块)。</p></li>
|
||
<li><p>编写基准测试脚本,在寻求性能提升的过程中就能实现快速迭代(请参阅 <a class="reference internal" href="../library/timeit.html#module-timeit" title="timeit: Measure the execution time of small code snippets."><code class="xref py py-mod docutils literal notranslate"><span class="pre">timeit</span></code></a> 模块)。</p></li>
|
||
<li><p>强烈建议首先要保证足够高的代码测试覆盖率(通过单元测试或其他技术),因为复杂的优化有可能会导致代码回退。</p></li>
|
||
</ul>
|
||
<p>话虽如此,Python 代码的提速还是有很多技巧的。以下列出了一些普适性的原则,对于让性能达到可接受的水平会有很大帮助:</p>
|
||
<ul class="simple">
|
||
<li><p>相较于试图对全部代码铺开做微观优化,优化算法(或换用更快的算法)可以产出更大的收益。</p></li>
|
||
<li><p>使用正确的数据结构。参考 <a class="reference internal" href="../library/stdtypes.html#bltin-types"><span class="std std-ref">内置类型</span></a> 和 <a class="reference internal" href="../library/collections.html#module-collections" title="collections: Container datatypes"><code class="xref py py-mod docutils literal notranslate"><span class="pre">collections</span></code></a> 模块的文档。</p></li>
|
||
<li><p>如果标准库已为某些操作提供了基础函数,则可能(当然不能保证)比所有自编的函数都要快。对于用 C 语言编写的基础函数则更是如此,比如内置函数和一些扩展类型。例如,一定要用内置方法 <a class="reference internal" href="../library/stdtypes.html#list.sort" title="list.sort"><code class="xref py py-meth docutils literal notranslate"><span class="pre">list.sort()</span></code></a> 或 <a class="reference internal" href="../library/functions.html#sorted" title="sorted"><code class="xref py py-func docutils literal notranslate"><span class="pre">sorted()</span></code></a> 函数进行排序(某些高级用法的示例请参阅 <a class="reference internal" href="../howto/sorting.html#sortinghowto"><span class="std std-ref">排序指南</span></a> )。</p></li>
|
||
<li><p>抽象往往会造成中间层,并会迫使解释器执行更多的操作。如果抽象出来的中间层级太多,工作量超过了要完成的有效任务,那么程序就会被拖慢。应该避免过度的抽象,而且往往也会对可读性产生不利影响,特别是当函数或方法比较小的时候。</p></li>
|
||
</ul>
|
||
<p>如果你已经达到纯 Python 允许的限制,那么有一些工具可以让你走得更远。 例如, <a class="reference external" href="http://cython.org">Cython</a> 可以将稍微修改的 Python 代码版本编译为 C 扩展,并且可以在许多不同的平台上使用。 Cython 可以利用编译(和可选的类型注释)来使代码明显快于解释运行时的速度。 如果您对 C 编程技能有信心,也可以自己 <a class="reference internal" href="../extending/index.html#extending-index"><span class="std std-ref">编写 C 扩展模块</span></a> 。</p>
|
||
<div class="admonition seealso">
|
||
<p class="admonition-title">参见</p>
|
||
<p>专门介绍 <a class="reference external" href="https://wiki.python.org/moin/PythonSpeed/PerformanceTips">性能提示</a> 的wiki页面。</p>
|
||
</div>
|
||
</section>
|
||
<section id="what-is-the-most-efficient-way-to-concatenate-many-strings-together">
|
||
<span id="efficient-string-concatenation"></span><h3><a class="toc-backref" href="#id39">将多个字符串连接在一起的最有效方法是什么?</a><a class="headerlink" href="#what-is-the-most-efficient-way-to-concatenate-many-strings-together" title="永久链接至标题">¶</a></h3>
|
||
<p><a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a> 和 <a class="reference internal" href="../library/stdtypes.html#bytes" title="bytes"><code class="xref py py-class docutils literal notranslate"><span class="pre">bytes</span></code></a> 对象是不可变的,因此连接多个字符串的效率会很低,因为每次连接都会创建一个新的对象。一般情况下,总耗时与字符串总长是二次方的关系。</p>
|
||
<p>如果要连接多个 <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a> 对象,通常推荐的方案是先全部放入列表,最后再调用 <a class="reference internal" href="../library/stdtypes.html#str.join" title="str.join"><code class="xref py py-meth docutils literal notranslate"><span class="pre">str.join()</span></code></a> :</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">my_strings</span><span class="p">:</span>
|
||
<span class="n">chunks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(还有一种合理高效的习惯做法,就是利用 <a class="reference internal" href="../library/io.html#io.StringIO" title="io.StringIO"><code class="xref py py-class docutils literal notranslate"><span class="pre">io.StringIO</span></code></a> )</p>
|
||
<p>如果要连接多个 <a class="reference internal" href="../library/stdtypes.html#bytes" title="bytes"><code class="xref py py-class docutils literal notranslate"><span class="pre">bytes</span></code></a> 对象,推荐做法是用 <a class="reference internal" href="../library/stdtypes.html#bytearray" title="bytearray"><code class="xref py py-class docutils literal notranslate"><span class="pre">bytearray</span></code></a> 对象的原地连接操作( <code class="docutils literal notranslate"><span class="pre">+=</span></code> 运算符)追加数据:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
|
||
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">my_bytes_objects</span><span class="p">:</span>
|
||
<span class="n">result</span> <span class="o">+=</span> <span class="n">b</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="sequences-tuples-lists">
|
||
<h2><a class="toc-backref" href="#id40">序列(元组/列表)</a><a class="headerlink" href="#sequences-tuples-lists" title="永久链接至标题">¶</a></h2>
|
||
<section id="how-do-i-convert-between-tuples-and-lists">
|
||
<h3><a class="toc-backref" href="#id41">如何在元组和列表之间进行转换?</a><a class="headerlink" href="#how-do-i-convert-between-tuples-and-lists" title="永久链接至标题">¶</a></h3>
|
||
<p>类型构造器 <code class="docutils literal notranslate"><span class="pre">tuple(seq)</span></code> 可将任意序列(实际上是任意可迭代对象)转换为数据项和顺序均不变的元组。</p>
|
||
<p>例如,<code class="docutils literal notranslate"><span class="pre">tuple([1,</span> <span class="pre">2,</span> <span class="pre">3])</span></code> 会生成 <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">2,</span> <span class="pre">3)</span></code> , <code class="docutils literal notranslate"><span class="pre">tuple('abc')</span></code> 则会生成 <code class="docutils literal notranslate"><span class="pre">('a',</span> <span class="pre">'b',</span> <span class="pre">'c')</span></code> 。 如果参数就是元组,则不会创建副本而是返回同一对象,因此如果无法确定某个对象是否为元组时,直接调用 <a class="reference internal" href="../library/stdtypes.html#tuple" title="tuple"><code class="xref py py-func docutils literal notranslate"><span class="pre">tuple()</span></code></a> 也没什么代价。</p>
|
||
<p>类型构造器 <code class="docutils literal notranslate"><span class="pre">list(seq)</span></code> 可将任意序列或可迭代对象转换为数据项和顺序均不变的列表。例如,<code class="docutils literal notranslate"><span class="pre">list((1,</span> <span class="pre">2,</span> <span class="pre">3))</span></code> 会生成 <code class="docutils literal notranslate"><span class="pre">[1,</span> <span class="pre">2,</span> <span class="pre">3]</span></code> 而 <code class="docutils literal notranslate"><span class="pre">list('abc')</span></code> 则会生成 <code class="docutils literal notranslate"><span class="pre">['a',</span> <span class="pre">'b',</span> <span class="pre">'c']</span></code>。如果参数即为列表,则会像 <code class="docutils literal notranslate"><span class="pre">seq[:]</span></code> 那样创建一个副本。</p>
|
||
</section>
|
||
<section id="what-s-a-negative-index">
|
||
<h3><a class="toc-backref" href="#id42">什么是负数索引?</a><a class="headerlink" href="#what-s-a-negative-index" title="永久链接至标题">¶</a></h3>
|
||
<p>Python 序列的索引可以是正数或负数。索引为正数时,0 是第一个索引值, 1 为第二个,依此类推。索引为负数时,-1 为倒数第一个索引值,-2 为倒数第二个,依此类推。可以认为 <code class="docutils literal notranslate"><span class="pre">seq[-n]</span></code> 就相当于 <code class="docutils literal notranslate"><span class="pre">seq[len(seq)-n]</span></code>。</p>
|
||
<p>使用负数序号有时会很方便。 例如 <code class="docutils literal notranslate"><span class="pre">S[:-1]</span></code> 就是原字符串去掉最后一个字符,这可以用来移除某个字符串末尾的换行符。</p>
|
||
</section>
|
||
<section id="how-do-i-iterate-over-a-sequence-in-reverse-order">
|
||
<h3><a class="toc-backref" href="#id43">序列如何以逆序遍历?</a><a class="headerlink" href="#how-do-i-iterate-over-a-sequence-in-reverse-order" title="永久链接至标题">¶</a></h3>
|
||
<p>使用内置函数 <a class="reference internal" href="../library/functions.html#reversed" title="reversed"><code class="xref py py-func docutils literal notranslate"><span class="pre">reversed()</span></code></a> :</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">sequence</span><span class="p">):</span>
|
||
<span class="o">...</span> <span class="c1"># do something with x ...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>原序列不会变化,而是构建一个逆序的新副本以供遍历。</p>
|
||
</section>
|
||
<section id="how-do-you-remove-duplicates-from-a-list">
|
||
<h3><a class="toc-backref" href="#id44">如何从列表中删除重复项?</a><a class="headerlink" href="#how-do-you-remove-duplicates-from-a-list" title="永久链接至标题">¶</a></h3>
|
||
<p>许多完成此操作的的详细介绍,可参阅 Python Cookbook:</p>
|
||
<blockquote>
|
||
<div><p><a class="reference external" href="https://code.activestate.com/recipes/52560/">https://code.activestate.com/recipes/52560/</a></p>
|
||
</div></blockquote>
|
||
<p>如果列表允许重新排序,不妨先对其排序,然后从列表末尾开始扫描,依次删除重复项:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">mylist</span><span class="p">:</span>
|
||
<span class="n">mylist</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
|
||
<span class="n">last</span> <span class="o">=</span> <span class="n">mylist</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">mylist</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">last</span> <span class="o">==</span> <span class="n">mylist</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
|
||
<span class="k">del</span> <span class="n">mylist</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">last</span> <span class="o">=</span> <span class="n">mylist</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>如果列表的所有元素都能用作集合的键(即都是 <a class="reference internal" href="../glossary.html#term-hashable"><span class="xref std std-term">hashable</span></a> ),以下做法速度往往更快:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">mylist</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">mylist</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>以上操作会将列表转换为集合,从而删除重复项,然后返回成列表。</p>
|
||
</section>
|
||
<section id="how-do-you-remove-multiple-items-from-a-list">
|
||
<h3><a class="toc-backref" href="#id45">如何从列表中删除多个项?</a><a class="headerlink" href="#how-do-you-remove-multiple-items-from-a-list" title="永久链接至标题">¶</a></h3>
|
||
<p>类似于删除重复项,一种做法是反向遍历并根据条件删除。不过更简单快速的做法就是切片替换操作,采用隐式或显式的正向迭代遍历。以下是三种变体写法:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">mylist</span><span class="p">[:]</span> <span class="o">=</span> <span class="nb">filter</span><span class="p">(</span><span class="n">keep_function</span><span class="p">,</span> <span class="n">mylist</span><span class="p">)</span>
|
||
<span class="n">mylist</span><span class="p">[:]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">mylist</span> <span class="k">if</span> <span class="n">keep_condition</span><span class="p">)</span>
|
||
<span class="n">mylist</span><span class="p">[:]</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">mylist</span> <span class="k">if</span> <span class="n">keep_condition</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>列表推导式可能是最快的。</p>
|
||
</section>
|
||
<section id="how-do-you-make-an-array-in-python">
|
||
<h3><a class="toc-backref" href="#id46">如何在 Python 中创建数组?</a><a class="headerlink" href="#how-do-you-make-an-array-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>用列表:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="s2">"this"</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"is"</span><span class="p">,</span> <span class="s2">"an"</span><span class="p">,</span> <span class="s2">"array"</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>列表在时间复杂度方面相当于 C 或 Pascal 的数组;主要区别在于,Python 列表可以包含多种不同类型的对象。</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">array</span></code> 模块还提供了创建具有紧凑表示的固定类型的数组的方法,但它的索引速度比列表慢。还要注意,数字扩展和其他扩展还定义了具有各种特性的类似数组的结构。</p>
|
||
<p>若要得到 Lisp 风格的列表,可以用元组模拟 cons 元素:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">lisp_list</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"like"</span><span class="p">,</span> <span class="p">(</span><span class="s2">"this"</span><span class="p">,</span> <span class="p">(</span><span class="s2">"example"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="p">)</span> <span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>若要具备可变性,可以不用元组而是用列表。模拟 lisp car 函数的是 <code class="docutils literal notranslate"><span class="pre">lisp_list[0]</span></code> ,模拟 cdr 函数的是 <code class="docutils literal notranslate"><span class="pre">lisp_list[1]</span></code> 。仅当真正必要时才会这么用,因为通常这种用法要比 Python 列表慢得多。</p>
|
||
</section>
|
||
<section id="how-do-i-create-a-multidimensional-list">
|
||
<span id="faq-multidimensional-list"></span><h3><a class="toc-backref" href="#id47">如何创建多维列表?</a><a class="headerlink" href="#how-do-i-create-a-multidimensional-list" title="永久链接至标题">¶</a></h3>
|
||
<p>多维数组或许会用以下方式建立:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span> <span class="o">=</span> <span class="p">[[</span><span class="kc">None</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o">*</span> <span class="mi">3</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>打印出来貌似没错:</p>
|
||
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span>
|
||
<span class="go">[[None, None], [None, None], [None, None]]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>但如果给某一项赋值,结果会同时在多个位置体现出来:</p>
|
||
<div class="highlight-pycon3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
|
||
<span class="gp">>>> </span><span class="n">A</span>
|
||
<span class="go">[[5, None], [5, None], [5, None]]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>原因在于用 <code class="docutils literal notranslate"><span class="pre">*</span></code> 对列表执行重复操作并不会创建副本,而只是创建现有对象的引用。 <code class="docutils literal notranslate"><span class="pre">*3</span></code> 创建的是包含 3 个引用的列表,每个引用指向的是同一个长度为 2 的列表。1 处改动会体现在所有地方,这一定不是应有的方案。</p>
|
||
<p>推荐做法是先创建一个所需长度的列表,然后将每个元素都填充为一个新建列表。</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">A</span> <span class="o">=</span> <span class="p">[</span><span class="kc">None</span><span class="p">]</span> <span class="o">*</span> <span class="mi">3</span>
|
||
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
|
||
<span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="kc">None</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>以上生成了一个包含 3 个列表的列表,每个子列表的长度为 2。也可以采用列表推导式:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span>
|
||
<span class="n">A</span> <span class="o">=</span> <span class="p">[[</span><span class="kc">None</span><span class="p">]</span> <span class="o">*</span> <span class="n">w</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">h</span><span class="p">)]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>或者你还可以使用提供矩阵类型的扩展包;其中最著名的是 <a class="reference external" href="http://www.numpy.org/">NumPy</a>。</p>
|
||
</section>
|
||
<section id="how-do-i-apply-a-method-to-a-sequence-of-objects">
|
||
<h3><a class="toc-backref" href="#id48">如何将方法应用于一系列对象?</a><a class="headerlink" href="#how-do-i-apply-a-method-to-a-sequence-of-objects" title="永久链接至标题">¶</a></h3>
|
||
<p>可以使用列表推导式:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">method</span><span class="p">()</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">mylist</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works">
|
||
<span id="faq-augmented-assignment-tuple-error"></span><h3><a class="toc-backref" href="#id49">为什么 a_tuple[i] += ['item'] 会引发异常?</a><a class="headerlink" href="#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works" title="永久链接至标题">¶</a></h3>
|
||
<p>这是由两个因素共同导致的,一是增强赋值运算符属于 <em>赋值</em> 运算符,二是 Python 可变和不可变对象之间的差别。</p>
|
||
<p>只要元组的元素指向可变对象,这时对元素进行增强赋值,那么这里介绍的内容都是适用的。在此只以 <code class="docutils literal notranslate"><span class="pre">list</span></code> 和 <code class="docutils literal notranslate"><span class="pre">+=</span></code> 举例。</p>
|
||
<p>如果你写成这样:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a_tuple</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
<span class="w"> </span><span class="c">...</span>
|
||
<span class="gr">TypeError</span>: <span class="n">'tuple' object does not support item assignment</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>触发异常的原因显而易见: <code class="docutils literal notranslate"><span class="pre">1</span></code> 会与指向(<code class="docutils literal notranslate"><span class="pre">1</span></code>)的对象 <code class="docutils literal notranslate"><span class="pre">a_tuple[0]</span></code> 相加,生成结果对象 <code class="docutils literal notranslate"><span class="pre">2</span></code>,但在试图将运算结果 <code class="docutils literal notranslate"><span class="pre">2</span></code> 赋值给元组的 <code class="docutils literal notranslate"><span class="pre">0</span></code> 号元素时就会报错,因为元组元素的指向无法更改。</p>
|
||
<p>其实在幕后,上述增强赋值语句的执行过程大致如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">result</span> <span class="o">=</span> <span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
|
||
<span class="gp">>>> </span><span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
<span class="w"> </span><span class="c">...</span>
|
||
<span class="gr">TypeError</span>: <span class="n">'tuple' object does not support item assignment</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>由于元组是不可变的,因此赋值这步会引发错误。</p>
|
||
<p>如果写成以下这样:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a_tuple</span> <span class="o">=</span> <span class="p">([</span><span class="s1">'foo'</span><span class="p">],</span> <span class="s1">'bar'</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+=</span> <span class="p">[</span><span class="s1">'item'</span><span class="p">]</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
<span class="w"> </span><span class="c">...</span>
|
||
<span class="gr">TypeError</span>: <span class="n">'tuple' object does not support item assignment</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这时触发异常会令人略感惊讶,更让人吃惊的是虽有报错,但加法操作却生效了:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
<span class="go">['foo', 'item']</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>要明白为何会这样,需要知道 (a) 如果一个对象实现了 <code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> 魔法方法,在执行 <code class="docutils literal notranslate"><span class="pre">+=</span></code> 增强赋值时就会调用它,并采纳其返回值;(b) 对于列表而言,<code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> 相当于在列表上调用 <code class="docutils literal notranslate"><span class="pre">extend</span></code> 并返回该列表。因此对于列表可以说 <code class="docutils literal notranslate"><span class="pre">+=</span></code> 就是 <code class="docutils literal notranslate"><span class="pre">list.extend</span></code> 的“快捷方式”:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a_list</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="gp">>>> </span><span class="n">a_list</span> <span class="o">+=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">a_list</span>
|
||
<span class="go">[1]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这相当于:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">result</span> <span class="o">=</span> <span class="n">a_list</span><span class="o">.</span><span class="fm">__iadd__</span><span class="p">([</span><span class="mi">1</span><span class="p">])</span>
|
||
<span class="gp">>>> </span><span class="n">a_list</span> <span class="o">=</span> <span class="n">result</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>a_list 所引用的对象已被修改,而引用被修改对象的指针又重新被赋值给 <code class="docutils literal notranslate"><span class="pre">a_list</span></code>。 赋值的最终结果没有变化,因为它是引用 <code class="docutils literal notranslate"><span class="pre">a_list</span></code> 之前所引用的同一对象的指针,但仍然发生了赋值操作。</p>
|
||
<p>因此,在此元组示例中,发生的事情等同于:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">result</span> <span class="o">=</span> <span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="fm">__iadd__</span><span class="p">([</span><span class="s1">'item'</span><span class="p">])</span>
|
||
<span class="gp">>>> </span><span class="n">a_tuple</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span>
|
||
<span class="gt">Traceback (most recent call last):</span>
|
||
<span class="w"> </span><span class="c">...</span>
|
||
<span class="gr">TypeError</span>: <span class="n">'tuple' object does not support item assignment</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> 成功执行,因此列表得到了扩充,但是虽然 <code class="docutils literal notranslate"><span class="pre">result</span></code> 指向了 <code class="docutils literal notranslate"><span class="pre">a_tuple[0]</span></code> 已经指向的同一对象,最后的赋值仍然导致了报错,因为元组是不可变的。</p>
|
||
</section>
|
||
<section id="i-want-to-do-a-complicated-sort-can-you-do-a-schwartzian-transform-in-python">
|
||
<h3><a class="toc-backref" href="#id50">我想做一个复杂的排序:能用 Python 进行施瓦茨变换吗?</a><a class="headerlink" href="#i-want-to-do-a-complicated-sort-can-you-do-a-schwartzian-transform-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>归功于 Perl 社区的 Randal Schwartz,该技术根据度量值对列表进行排序,该度量值将每个元素映射为“顺序值”。在 Python 中,请利用 <a class="reference internal" href="../library/stdtypes.html#list.sort" title="list.sort"><code class="xref py py-meth docutils literal notranslate"><span class="pre">list.sort()</span></code></a> 方法的 <code class="docutils literal notranslate"><span class="pre">key</span></code> 参数:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">Isorted</span> <span class="o">=</span> <span class="n">L</span><span class="p">[:]</span>
|
||
<span class="n">Isorted</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="mi">10</span><span class="p">:</span><span class="mi">15</span><span class="p">]))</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-can-i-sort-one-list-by-values-from-another-list">
|
||
<h3><a class="toc-backref" href="#id51">如何根据另一个列表的值对某列表进行排序?</a><a class="headerlink" href="#how-can-i-sort-one-list-by-values-from-another-list" title="永久链接至标题">¶</a></h3>
|
||
<p>将它们合并到元组的迭代器中,对结果列表进行排序,然后选择所需的元素。</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">list1</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"what"</span><span class="p">,</span> <span class="s2">"I'm"</span><span class="p">,</span> <span class="s2">"sorting"</span><span class="p">,</span> <span class="s2">"by"</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">list2</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"something"</span><span class="p">,</span> <span class="s2">"else"</span><span class="p">,</span> <span class="s2">"to"</span><span class="p">,</span> <span class="s2">"sort"</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">pairs</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="n">list1</span><span class="p">,</span> <span class="n">list2</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">pairs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">pairs</span>
|
||
<span class="go">[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]</span>
|
||
<span class="gp">>>> </span><span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">pairs</span><span class="p">]</span>
|
||
<span class="gp">>>> </span><span class="n">result</span>
|
||
<span class="go">['else', 'sort', 'to', 'something']</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>最后一步的替代方案是:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="gp">>>> </span><span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pairs</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>如果你觉得这个更容易读懂,那么你可能更喜欢使用这个而不是前面的列表推导。然而,对于长列表来说,它的速度几乎是原来的两倍。为什么?首先, <code class="docutils literal notranslate"><span class="pre">append()</span></code> 操作必须重新分配内存,虽然它使用了一些技巧来避免每次都这样做,但它仍然偶尔需要这样做,而且代价相当高。第二,表达式 "result.append" 需要额外的属性查找。第三,必须执行所有这些函数调用会降低速度。</p>
|
||
</section>
|
||
</section>
|
||
<section id="objects">
|
||
<h2><a class="toc-backref" href="#id52">对象</a><a class="headerlink" href="#objects" title="永久链接至标题">¶</a></h2>
|
||
<section id="what-is-a-class">
|
||
<h3><a class="toc-backref" href="#id53">什么是类?</a><a class="headerlink" href="#what-is-a-class" title="永久链接至标题">¶</a></h3>
|
||
<p>类是通过执行 class 语句创建的某种对象的类型。创建实例对象时,用 Class 对象作为模板,实例对象既包含了数据(属性),又包含了数据类型特有的代码(方法)。</p>
|
||
<p>类可以基于一个或多个其他类(称之为基类)进行创建。基类的属性和方法都得以继承。这样对象模型就可以通过继承不断地进行细化。比如通用的 <code class="docutils literal notranslate"><span class="pre">Mailbox</span></code> 类提供了邮箱的基本访问方法.,它的子类 <code class="docutils literal notranslate"><span class="pre">MboxMailbox</span></code>、 <code class="docutils literal notranslate"><span class="pre">MaildirMailbox</span></code>、 <code class="docutils literal notranslate"><span class="pre">OutlookMailbox</span></code> 则能够处理各种特定的邮箱格式。</p>
|
||
</section>
|
||
<section id="what-is-a-method">
|
||
<h3><a class="toc-backref" href="#id54">什么是方法?</a><a class="headerlink" href="#what-is-a-method" title="永久链接至标题">¶</a></h3>
|
||
<p>方法是属于对象的函数,对于对象 <code class="docutils literal notranslate"><span class="pre">x</span></code> ,通常以 <code class="docutils literal notranslate"><span class="pre">x.name(arguments...)</span></code> 的形式调用。方法以函数的形式给出定义,位于类的定义内:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">arg</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">attribute</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="what-is-self">
|
||
<h3><a class="toc-backref" href="#id55">什么是 self ?</a><a class="headerlink" href="#what-is-self" title="永久链接至标题">¶</a></h3>
|
||
<p>Self 只是方法的第一个参数的习惯性名称。假定某个类中有个方法定义为 <code class="docutils literal notranslate"><span class="pre">meth(self,</span> <span class="pre">a,</span> <span class="pre">b,</span> <span class="pre">c)</span></code> ,则其实例 <code class="docutils literal notranslate"><span class="pre">x</span></code> 应以 <code class="docutils literal notranslate"><span class="pre">x.meth(a,</span> <span class="pre">b,</span> <span class="pre">c)</span></code> 的形式进行调用;而被调用的方法则应视其为做了 <code class="docutils literal notranslate"><span class="pre">meth(x,</span> <span class="pre">a,</span> <span class="pre">b,</span> <span class="pre">c)</span></code> 形式的调用。</p>
|
||
<p>另请参阅 <a class="reference internal" href="design.html#why-self"><span class="std std-ref">为什么必须在方法定义和调用中显式使用“self”?</span></a> 。</p>
|
||
</section>
|
||
<section id="how-do-i-check-if-an-object-is-an-instance-of-a-given-class-or-of-a-subclass-of-it">
|
||
<h3><a class="toc-backref" href="#id56">如何检查对象是否为给定类或其子类的一个实例?</a><a class="headerlink" href="#how-do-i-check-if-an-object-is-an-instance-of-a-given-class-or-of-a-subclass-of-it" title="永久链接至标题">¶</a></h3>
|
||
<p>可使用内置函数 <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">cls)</span></code>。可以检测对象是否属于多个类中某一个的实例,只要把单个类换成元组即可,比如 <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">(class1,</span> <span class="pre">class2,</span> <span class="pre">...))</span></code>,还可以检查对象是否属于某个 Python 内置类型,例如 <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">str)</span></code> 或 <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">(int,</span> <span class="pre">float,</span> <span class="pre">complex))</span></code>。</p>
|
||
<p>请注意,大多数程序不会经常用 <a class="reference internal" href="../library/functions.html#isinstance" title="isinstance"><code class="xref py py-func docutils literal notranslate"><span class="pre">isinstance()</span></code></a> 对用户自定义类进行检测。 如果是自已开发的类,更合适的面向对象编程风格应该是在类中定义多种方法,以封装特定的行为,而不是检查对象属于什么类再据此干不同的事。假定有如下执行某些操作的函数:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">Mailbox</span><span class="p">):</span>
|
||
<span class="o">...</span> <span class="c1"># code to search a mailbox</span>
|
||
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">Document</span><span class="p">):</span>
|
||
<span class="o">...</span> <span class="c1"># code to search a document</span>
|
||
<span class="k">elif</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>更好的方法是在所有类上定义一个 <code class="docutils literal notranslate"><span class="pre">search()</span></code> 方法,然后调用它:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Mailbox</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="o">...</span> <span class="c1"># code to search a mailbox</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Document</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="o">...</span> <span class="c1"># code to search a document</span>
|
||
|
||
<span class="n">obj</span><span class="o">.</span><span class="n">search</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="what-is-delegation">
|
||
<h3><a class="toc-backref" href="#id57">什么是委托?</a><a class="headerlink" href="#what-is-delegation" title="永久链接至标题">¶</a></h3>
|
||
<p>委托是一种面向对象的技术(也称为设计模式)。假设对象 <code class="docutils literal notranslate"><span class="pre">x</span></code> 已经存在,现在想要改变其某个方法的行为。可以创建一个新类,其中提供了所需修改方法的新实现,而将所有其他方法都委托给 <code class="docutils literal notranslate"><span class="pre">x</span></code> 的对应方法。</p>
|
||
<p>Python 程序员可以轻松实现委托。比如以下实现了一个类似于文件的类,只是会把所有写入的数据转换为大写:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">UpperOut</span><span class="p">:</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">outfile</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_outfile</span> <span class="o">=</span> <span class="n">outfile</span>
|
||
|
||
<span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">s</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_outfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">upper</span><span class="p">())</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__getattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_outfile</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这里 <code class="docutils literal notranslate"><span class="pre">UpperOut</span></code> 类重新定义了 <code class="docutils literal notranslate"><span class="pre">write()</span></code> 方法,在调用下层的 <code class="docutils literal notranslate"><span class="pre">self._outfile.write()</span></code> 方法之前,会将参数字符串转换为大写。其他所有方法则都被委托给下层的 <code class="docutils literal notranslate"><span class="pre">self._outfile</span></code> 对象。委托是通过 <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> 方法完成的;请参阅 <a class="reference internal" href="../reference/datamodel.html#attribute-access"><span class="std std-ref">语言参考</span></a> 了解有关控制属性访问的更多信息。</p>
|
||
<p>请注意,更常见情况下,委托可能会变得比较棘手。如果属性既需要写入又需要读取,那么类还必须定义 <a class="reference internal" href="../reference/datamodel.html#object.__setattr__" title="object.__setattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__setattr__()</span></code></a> 方法,而这时就必须十分的小心。基础的 <a class="reference internal" href="../reference/datamodel.html#object.__setattr__" title="object.__setattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__setattr__()</span></code></a> 实现代码大致如下:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">X</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>大多数 <a class="reference internal" href="../reference/datamodel.html#object.__setattr__" title="object.__setattr__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__setattr__()</span></code></a> 实现必须修改 <code class="docutils literal notranslate"><span class="pre">self.__dict__</span></code> 来为自身保存局部状态,而不至于引起无限递归。</p>
|
||
</section>
|
||
<section id="how-do-i-call-a-method-defined-in-a-base-class-from-a-derived-class-that-overrides-it">
|
||
<h3><a class="toc-backref" href="#id58">如何在派生类中调用被重载的基类方法?</a><a class="headerlink" href="#how-do-i-call-a-method-defined-in-a-base-class-from-a-derived-class-that-overrides-it" title="永久链接至标题">¶</a></h3>
|
||
<p>使用内置的 <a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a> 函数:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Derived</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="nb">super</span><span class="p">(</span><span class="n">Derived</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">meth</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>如果是 Python 3.0 之前的版本,可能用的还是传统类:对于诸如 <code class="docutils literal notranslate"><span class="pre">class</span> <span class="pre">Derived(Base):</span> <span class="pre">...</span></code> 之类的类定义,可以用 <code class="docutils literal notranslate"><span class="pre">Base.meth(self,</span> <span class="pre">arguments...)</span></code> 的形式调用 <code class="docutils literal notranslate"><span class="pre">Base</span></code> (或 <code class="docutils literal notranslate"><span class="pre">Base</span></code> 的某个基类)中定义的方法 <code class="docutils literal notranslate"><span class="pre">meth()</span></code> 。这里, <code class="docutils literal notranslate"><span class="pre">Base.meth</span></code> 是一个未绑定的方法,因此需要给出 <code class="docutils literal notranslate"><span class="pre">self</span></code> 参数。</p>
|
||
</section>
|
||
<section id="how-can-i-organize-my-code-to-make-it-easier-to-change-the-base-class">
|
||
<h3><a class="toc-backref" href="#id59">如何让代码更容易对基类进行修改?</a><a class="headerlink" href="#how-can-i-organize-my-code-to-make-it-easier-to-change-the-base-class" title="永久链接至标题">¶</a></h3>
|
||
<p>可以为基类定义别名,在类定义之前为其分配实际基类,并在整个类中使用别名。然后更改分配给别名的值,就能实现上述要求。顺便提一下,如果你想动态决定(例如,取决于资源的可用性)要使用哪个基类,这个技巧也很方便。例如:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">BaseAlias</span> <span class="o">=</span> <span class="o"><</span><span class="n">real</span> <span class="n">base</span> <span class="n">class</span><span class="o">></span>
|
||
|
||
<span class="k">class</span> <span class="nc">Derived</span><span class="p">(</span><span class="n">BaseAlias</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">BaseAlias</span><span class="o">.</span><span class="n">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-do-i-create-static-class-data-and-static-class-methods">
|
||
<h3><a class="toc-backref" href="#id60">如何创建静态类数据和静态类方法?</a><a class="headerlink" href="#how-do-i-create-static-class-data-and-static-class-methods" title="永久链接至标题">¶</a></h3>
|
||
<p>Python 支持静态数据和静态方法(以 C++ 或 Java 的定义而言)。</p>
|
||
<p>静态数据只需定义一个类属性即可。若要为属性赋新值,则必须在赋值时显式使用类名:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># number of times C.__init__ called</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">C</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="n">C</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span>
|
||
|
||
<span class="k">def</span> <span class="nf">getcount</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">C</span><span class="o">.</span><span class="n">count</span> <span class="c1"># or return self.count</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>对于所有符合 <code class="docutils literal notranslate"><span class="pre">isinstance(c,</span> <span class="pre">C)</span></code> 的 <code class="docutils literal notranslate"><span class="pre">c</span></code>, <code class="docutils literal notranslate"><span class="pre">c.count</span></code> 也同样指向 <code class="docutils literal notranslate"><span class="pre">C.count</span></code> ,除非被 <code class="docutils literal notranslate"><span class="pre">c</span></code> 自身或者被从 <code class="docutils literal notranslate"><span class="pre">c.__class__</span></code> 回溯到基类 <code class="docutils literal notranslate"><span class="pre">C</span></code> 的搜索路径上的某个类所覆盖。</p>
|
||
<p>注意:在 C 的某个方法内部,像 <code class="docutils literal notranslate"><span class="pre">self.count</span> <span class="pre">=</span> <span class="pre">42</span></code> 这样的赋值将在 <code class="docutils literal notranslate"><span class="pre">self</span></code> 自身的字典中新建一个名为 "count" 的不相关实例。 想要重新绑定类静态数据名称就必须总是指明类名,无论是在方法内部还是外部:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">C</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="mi">314</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Python 支持静态方法:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="nd">@staticmethod</span>
|
||
<span class="k">def</span> <span class="nf">static</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">):</span>
|
||
<span class="c1"># No 'self' parameter!</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>不过为了获得静态方法的效果,还有一种做法直接得多,也即使用模块级函数即可:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getcount</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="n">C</span><span class="o">.</span><span class="n">count</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>如果代码的结构化比较充分,每个模块只定义了一个类(或者多个类的层次关系密切相关),那就具备了应有的封装。</p>
|
||
</section>
|
||
<section id="how-can-i-overload-constructors-or-methods-in-python">
|
||
<h3><a class="toc-backref" href="#id61">在 Python 中如何重载构造函数(或方法)?</a><a class="headerlink" href="#how-can-i-overload-constructors-or-methods-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>这个答案实际上适用于所有方法,但问题通常首先出现于构造函数的应用场景中。</p>
|
||
<p>在 C++ 中,代码会如下所示:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">class</span><span class="w"> </span><span class="n">C</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">C</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">cout</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="s">"No arguments</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="n">C</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">cout</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="s">"Argument is "</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>在 Python 中,只能编写一个构造函数,并用默认参数捕获所有情况。例如:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">i</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"No arguments"</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Argument is"</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这不完全等同,但在实践中足够接近。</p>
|
||
<p>也可以试试采用变长参数列表,例如:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>上述做法同样适用于所有方法定义。</p>
|
||
</section>
|
||
<section id="i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam">
|
||
<h3><a class="toc-backref" href="#id62">在用 __spam 的时候得到一个类似 _SomeClassName__spam 的错误信息。</a><a class="headerlink" href="#i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam" title="永久链接至标题">¶</a></h3>
|
||
<p>以双下划线打头的变量名会被“破坏”,以便以一种简单高效的方式定义类私有变量。任何形式为 <code class="docutils literal notranslate"><span class="pre">__spam</span></code> 的标识符(至少前缀两个下划线,至多后缀一个下划线)文本均会被替换为 <code class="docutils literal notranslate"><span class="pre">_classname__spam</span></code>,其中 <code class="docutils literal notranslate"><span class="pre">classname</span></code> 为去除了全部前缀下划线的当前类名称。</p>
|
||
<p>这并不能保证私密性:外部用户仍然可以访问 "_classname__spam" 属性,私有变量值也在对象的 <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> 中可见。 许多 Python 程序员根本不操心要去使用私有变量名。</p>
|
||
</section>
|
||
<section id="my-class-defines-del-but-it-is-not-called-when-i-delete-the-object">
|
||
<h3><a class="toc-backref" href="#id63">类定义了 __del__ 方法,但是删除对象时没有调用它。</a><a class="headerlink" href="#my-class-defines-del-but-it-is-not-called-when-i-delete-the-object" title="永久链接至标题">¶</a></h3>
|
||
<p>这有几个可能的原因。</p>
|
||
<p>del 语句不一定调用 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> —— 它只是减少对象的引用计数,如果(引用计数)达到零,才会调用 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a>。</p>
|
||
<p>如果数据结构包含循环链接(比如树的每个子节点都带有父节点的引用,而每个父节点也带有子节点的列表),则引用计数永远不会回零。尽管 Python 偶尔会用某种算法检测这种循环引用,但在数据结构的最后一条引用消失之后,垃圾收集器可能还要过段时间才会运行,因此 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法可能会在不方便和随机的时刻被调用。这对于重现一个问题,是非常不方便的。更糟糕的是,各个对象的 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法是以随机顺序执行的。虽然可以运行 <a class="reference internal" href="../library/gc.html#gc.collect" title="gc.collect"><code class="xref py py-func docutils literal notranslate"><span class="pre">gc.collect()</span></code></a> 来强制执行垃圾回收工作,但 <em>仍会存在</em> 一些对象永远不会被回收的失控情况。</p>
|
||
<p>尽管有垃圾回收器的存在,但为对象定义显式的 <code class="docutils literal notranslate"><span class="pre">close()</span></code> 方法,只要一用完即可供调用,这依然是一个好主意。这样 <code class="docutils literal notranslate"><span class="pre">close()</span></code> 方法即可删除引用子对象的属性。请勿直接调用 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> —— 而 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 应该调用 <code class="docutils literal notranslate"><span class="pre">close()</span></code>,并且应能确保可以对同一对象多次调用 <code class="docutils literal notranslate"><span class="pre">close()</span></code> 。</p>
|
||
<p>另一种避免循环引用的做法是利用 <a class="reference internal" href="../library/weakref.html#module-weakref" title="weakref: Support for weak references and weak dictionaries."><code class="xref py py-mod docutils literal notranslate"><span class="pre">weakref</span></code></a> 模块,该模块允许指向对象但不增加其引用计数。例如,树状数据结构应该对父节点和同级节点使用弱引用(如果真要用的话!)</p>
|
||
<p>最后提一下,如果 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法引发了异常,会将警告消息打印到 <a class="reference internal" href="../library/sys.html#sys.stderr" title="sys.stderr"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.stderr</span></code></a> 。</p>
|
||
</section>
|
||
<section id="how-do-i-get-a-list-of-all-instances-of-a-given-class">
|
||
<h3><a class="toc-backref" href="#id64">如何获取给定类的所有实例的列表?</a><a class="headerlink" href="#how-do-i-get-a-list-of-all-instances-of-a-given-class" title="永久链接至标题">¶</a></h3>
|
||
<p>Python 不会记录类(或内置类型)的实例。可以在类的构造函数中编写代码,通过保留每个实例的弱引用列表来跟踪所有实例。</p>
|
||
</section>
|
||
<section id="why-does-the-result-of-id-appear-to-be-not-unique">
|
||
<h3><a class="toc-backref" href="#id65">为什么 <code class="docutils literal notranslate"><span class="pre">id()</span></code> 的结果看起来不是唯一的?</a><a class="headerlink" href="#why-does-the-result-of-id-appear-to-be-not-unique" title="永久链接至标题">¶</a></h3>
|
||
<p><a class="reference internal" href="../library/functions.html#id" title="id"><code class="xref py py-func docutils literal notranslate"><span class="pre">id()</span></code></a> 返回一个整数,该整数在对象的生命周期内保证是唯一的。 因为在 CPython 中,这是对象的内存地址,所以经常发生在从内存中删除对象之后,下一个新创建的对象被分配在内存中的相同位置。 这个例子说明了这一点:</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">id</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
|
||
<span class="go">13901272</span>
|
||
<span class="gp">>>> </span><span class="nb">id</span><span class="p">(</span><span class="mi">2000</span><span class="p">)</span>
|
||
<span class="go">13901272</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这两个 id 属于不同的整数对象,之前先创建了对象,执行 <code class="docutils literal notranslate"><span class="pre">id()</span></code> 调用后又立即被删除了。若要确保检测 id 时的对象仍处于活动状态,请再创建一个对该对象的引用:</p>
|
||
<div class="doctest highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2000</span>
|
||
<span class="gp">>>> </span><span class="nb">id</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
|
||
<span class="go">13901272</span>
|
||
<span class="gp">>>> </span><span class="nb">id</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
|
||
<span class="go">13891296</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="modules">
|
||
<h2><a class="toc-backref" href="#id66">模块</a><a class="headerlink" href="#modules" title="永久链接至标题">¶</a></h2>
|
||
<section id="how-do-i-create-a-pyc-file">
|
||
<h3><a class="toc-backref" href="#id67">如何创建 .pyc 文件?</a><a class="headerlink" href="#how-do-i-create-a-pyc-file" title="永久链接至标题">¶</a></h3>
|
||
<p>当首次导入模块时(或当前已编译文件创建之后源文件发生了改动),在 <code class="docutils literal notranslate"><span class="pre">.py</span></code> 文件所在目录的 <code class="docutils literal notranslate"><span class="pre">__pycache__</span></code> 子目录下会创建一个包含已编译代码的 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件。该 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件的名称开头部分将与 <code class="docutils literal notranslate"><span class="pre">.py</span></code> 文件名相同,并以 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 为后缀,中间部分则依据创建它的 <code class="docutils literal notranslate"><span class="pre">python</span></code> 版本而各不相同。(详见 <span class="target" id="index-8"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-3147"><strong>PEP 3147</strong></a>。)</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件有可能会无法创建,原因之一是源码文件所在的目录存在权限问题,这样就无法创建 <code class="docutils literal notranslate"><span class="pre">__pycache__</span></code> 子目录。假如以某个用户开发程序而以另一用户运行程序,就有可能发生权限问题,测试 Web 服务器就属于这种情况。</p>
|
||
<p>除非设置了 <span class="target" id="index-9"></span><a class="reference internal" href="../using/cmdline.html#envvar-PYTHONDONTWRITEBYTECODE"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">PYTHONDONTWRITEBYTECODE</span></code></a> 环境变量,否则导入模块并且 Python 能够创建 <code class="docutils literal notranslate"><span class="pre">__pycache__</span></code> 子目录并把已编译模块写入该子目录(权限、存储空间等等)时,.pyc 文件就将自动创建。</p>
|
||
<p>在最高层级运行的 Python 脚本不会被视为经过了导入操作,因此不会创建 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件。假定有一个最高层级的模块文件 <code class="docutils literal notranslate"><span class="pre">foo.py</span></code>,它导入了另一个模块 <code class="docutils literal notranslate"><span class="pre">xyz.py</span></code>,当运行 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 模块(通过输入 shell 命令 <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">foo.py</span></code> ),则会为 <code class="docutils literal notranslate"><span class="pre">xyz</span></code> 创建一个 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code>,因为 <code class="docutils literal notranslate"><span class="pre">xyz</span></code> 是被导入的,但不会为 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 创建 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件,因为 <code class="docutils literal notranslate"><span class="pre">foo.py</span></code> 不是被导入的。</p>
|
||
<p>若要为 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 创建 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件 —— 即为未做导入的模块创建 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件 —— 可以利用 <a class="reference internal" href="../library/py_compile.html#module-py_compile" title="py_compile: Generate byte-code files from Python source files."><code class="xref py py-mod docutils literal notranslate"><span class="pre">py_compile</span></code></a> 和 <a class="reference internal" href="../library/compileall.html#module-compileall" title="compileall: Tools for byte-compiling all Python source files in a directory tree."><code class="xref py py-mod docutils literal notranslate"><span class="pre">compileall</span></code></a> 模块。</p>
|
||
<p><a class="reference internal" href="../library/py_compile.html#module-py_compile" title="py_compile: Generate byte-code files from Python source files."><code class="xref py py-mod docutils literal notranslate"><span class="pre">py_compile</span></code></a> 模块能够手动编译任意模块。 一种做法是交互式地使用该模块中的 <code class="docutils literal notranslate"><span class="pre">compile()</span></code> 函数:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">py_compile</span>
|
||
<span class="gp">>>> </span><span class="n">py_compile</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s1">'foo.py'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这将会将 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件写入与 <code class="docutils literal notranslate"><span class="pre">foo.py</span></code> 相同位置下的 <code class="docutils literal notranslate"><span class="pre">__pycache__</span></code> 子目录(或者你也可以通过可选参数 <code class="docutils literal notranslate"><span class="pre">cfile</span></code> 来重写该行为)。</p>
|
||
<p>还可以用 <a class="reference internal" href="../library/compileall.html#module-compileall" title="compileall: Tools for byte-compiling all Python source files in a directory tree."><code class="xref py py-mod docutils literal notranslate"><span class="pre">compileall</span></code></a> 模块自动编译一个或多个目录下的所有文件。只要在命令行提示符中运行 <code class="docutils literal notranslate"><span class="pre">compileall.py</span></code> 并给出要编译的 Python 文件所在目录路径即可:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">-</span><span class="n">m</span> <span class="n">compileall</span> <span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-do-i-find-the-current-module-name">
|
||
<h3><a class="toc-backref" href="#id68">如何找到当前模块名称?</a><a class="headerlink" href="#how-do-i-find-the-current-module-name" title="永久链接至标题">¶</a></h3>
|
||
<p>模块可以查看预定义的全局变量 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 获悉自己的名称。如其值为 <code class="docutils literal notranslate"><span class="pre">'__main__'</span></code> ,程序将作为脚本运行。通常,许多通过导入使用的模块同时也提供命令行接口或自检代码,这些代码只在检测到处于 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 之后才会执行:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s1">'Running test...'</span><span class="p">)</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
|
||
<span class="n">main</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="how-can-i-have-modules-that-mutually-import-each-other">
|
||
<h3><a class="toc-backref" href="#id69">如何让模块相互导入?</a><a class="headerlink" href="#how-can-i-have-modules-that-mutually-import-each-other" title="永久链接至标题">¶</a></h3>
|
||
<p>假设有以下模块:</p>
|
||
<p>foo.py:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">bar</span> <span class="kn">import</span> <span class="n">bar_var</span>
|
||
<span class="n">foo_var</span> <span class="o">=</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>bar.py:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">foo</span> <span class="kn">import</span> <span class="n">foo_var</span>
|
||
<span class="n">bar_var</span> <span class="o">=</span> <span class="mi">2</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>问题是解释器将执行以下步骤:</p>
|
||
<ul class="simple">
|
||
<li><p>首先导入foo</p></li>
|
||
<li><p>创建用于foo的空全局变量</p></li>
|
||
<li><p>foo被编译并开始执行</p></li>
|
||
<li><p>foo 导入 bar</p></li>
|
||
<li><p>创建了用于bar 的空全局变量</p></li>
|
||
<li><p>bar被编译并开始执行</p></li>
|
||
<li><p>bar 导入 foo(这是一个空操作,因为已经有一个名为 foo 的模块)。</p></li>
|
||
<li><p>bar.foo_var = foo.foo_var</p></li>
|
||
</ul>
|
||
<p>最后一步失败了,因为 Python 还没有完成对 foo 的解释,foo 的全局符号字典仍然是空的。</p>
|
||
<p>当你使用 <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">foo</span></code> ,然后尝试在全局代码中访问 <code class="docutils literal notranslate"><span class="pre">foo.foo_var</span></code> 时,会发生同样的事情。</p>
|
||
<p>这个问题有(至少)三种可能的解决方法。</p>
|
||
<p>Guido van Rossum 建议完全避免使用 <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre"><module></span> <span class="pre">import</span> <span class="pre">...</span></code> ,并将所有代码放在函数中。全局变量和类变量的初始化只应使用常量或内置函数。这意味着导入模块中的所有内容都以 <code class="docutils literal notranslate"><span class="pre"><module>.<name></span></code> 的形式引用。</p>
|
||
<p>Jim Roskind 建议每个模块都应遵循以下顺序:</p>
|
||
<ul class="simple">
|
||
<li><p>导出(全局变量、函数和不需要导入基类的类)</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">import</span></code> 语句</p></li>
|
||
<li><p>本模块的功能代码(包括根据导入值进行初始化的全局变量)。</p></li>
|
||
</ul>
|
||
<p>van Rossum不喜欢这种方法,因为导入出现在一个陌生的地方,但这种方法确实有效。</p>
|
||
<p>Matthias Urlichs 建议对代码进行重构,使得递归导入根本就没必要发生。</p>
|
||
<p>这些解决方案并不相互排斥。</p>
|
||
</section>
|
||
<section id="import-x-y-z-returns-module-x-how-do-i-get-z">
|
||
<h3><a class="toc-backref" href="#id70">__import__('x.y.z') 返回的是 <module 'x'> ;该如何得到 z 呢?</a><a class="headerlink" href="#import-x-y-z-returns-module-x-how-do-i-get-z" title="永久链接至标题">¶</a></h3>
|
||
<p>不妨考虑换用 <a class="reference internal" href="../library/importlib.html#module-importlib" title="importlib: The implementation of the import machinery."><code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a> 中的函数 <a class="reference internal" href="../library/importlib.html#importlib.import_module" title="importlib.import_module"><code class="xref py py-func docutils literal notranslate"><span class="pre">import_module()</span></code></a> :</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">z</span> <span class="o">=</span> <span class="n">importlib</span><span class="o">.</span><span class="n">import_module</span><span class="p">(</span><span class="s1">'x.y.z'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="when-i-edit-an-imported-module-and-reimport-it-the-changes-don-t-show-up-why-does-this-happen">
|
||
<h3><a class="toc-backref" href="#id71">对已导入的模块进行了编辑并重新导入,但变动没有得以体现。这是为什么?</a><a class="headerlink" href="#when-i-edit-an-imported-module-and-reimport-it-the-changes-don-t-show-up-why-does-this-happen" title="永久链接至标题">¶</a></h3>
|
||
<p>出于效率和一致性的原因,Python 仅在第一次导入模块时读取模块文件。否则,在一个多模块的程序中,每个模块都会导入相同的基础模块,那么基础模块将会被一而再、再而三地解析。如果要强行重新读取已更改的模块,请执行以下操作:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">importlib</span>
|
||
<span class="kn">import</span> <span class="nn">modname</span>
|
||
<span class="n">importlib</span><span class="o">.</span><span class="n">reload</span><span class="p">(</span><span class="n">modname</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>警告:这种技术并非万无一失。尤其是模块包含了以下语句时:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">modname</span> <span class="kn">import</span> <span class="n">some_objects</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>仍将继续使用前一版的导入对象。如果模块包含了类的定义,并 <em>不会</em> 用新的类定义更新现有的类实例。这样可能会导致以下矛盾的行为:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">importlib</span>
|
||
<span class="gp">>>> </span><span class="kn">import</span> <span class="nn">cls</span>
|
||
<span class="gp">>>> </span><span class="n">c</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">C</span><span class="p">()</span> <span class="c1"># Create an instance of C</span>
|
||
<span class="gp">>>> </span><span class="n">importlib</span><span class="o">.</span><span class="n">reload</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
||
<span class="go"><module 'cls' from 'cls.py'></span>
|
||
<span class="gp">>>> </span><span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="bp">cls</span><span class="o">.</span><span class="n">C</span><span class="p">)</span> <span class="c1"># isinstance is false?!?</span>
|
||
<span class="go">False</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>只要把类对象的 id 打印出来,问题的性质就会一目了然:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">hex</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="vm">__class__</span><span class="p">))</span>
|
||
<span class="go">'0x7352a0'</span>
|
||
<span class="gp">>>> </span><span class="nb">hex</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">C</span><span class="p">))</span>
|
||
<span class="go">'0x4198d0'</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<h3><a href="../contents.html">目录</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">编程常见问题</a><ul>
|
||
<li><a class="reference internal" href="#general-questions">一般问题</a></li>
|
||
<li><a class="reference internal" href="#core-language">语言核心内容</a></li>
|
||
<li><a class="reference internal" href="#numbers-and-strings">数字和字符串</a></li>
|
||
<li><a class="reference internal" href="#performance">性能</a></li>
|
||
<li><a class="reference internal" href="#sequences-tuples-lists">序列(元组/列表)</a></li>
|
||
<li><a class="reference internal" href="#objects">对象</a></li>
|
||
<li><a class="reference internal" href="#modules">模块</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>上一个主题</h4>
|
||
<p class="topless"><a href="general.html"
|
||
title="上一章">Python常见问题</a></p>
|
||
<h4>下一个主题</h4>
|
||
<p class="topless"><a href="design.html"
|
||
title="下一章">设计和历史常见问题</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>本页</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">报告 Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.8/Doc/faq/programming.rst"
|
||
rel="nofollow">显示源代码
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>导航</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="总目录"
|
||
>索引</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python 模块索引"
|
||
>模块</a> |</li>
|
||
<li class="right" >
|
||
<a href="design.html" title="设计和历史常见问题"
|
||
>下一页</a> |</li>
|
||
<li class="right" >
|
||
<a href="general.html" title="Python常见问题"
|
||
>上一页</a> |</li>
|
||
|
||
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li class="switchers">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<div class="version_switcher_placeholder"></div>
|
||
</li>
|
||
<li>
|
||
|
||
</li>
|
||
<li id="cpython-language-and-version">
|
||
<a href="../index.html">3.8.20 Documentation</a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" >Python 常见问题</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
|
||
<input type="submit" value="转向" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
<div class="footer">
|
||
© <a href="../copyright.html">版权所有</a> 2001-2024, Python Software Foundation.
|
||
<br />
|
||
This page is licensed under the Python Software Foundation License Version 2.
|
||
<br />
|
||
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
|
||
<br />
|
||
|
||
<br />
|
||
|
||
The Python Software Foundation is a non-profit corporation.
|
||
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
||
<br />
|
||
<br />
|
||
|
||
最后更新于 12月 09, 2024.
|
||
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
|
||
<br />
|
||
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 2.4.4.
|
||
</div>
|
||
|
||
</body>
|
||
</html> |