diff --git a/.github/workflows/honkit-depoly.yml b/.github/workflows/honkit-depoly.yml new file mode 100644 index 00000000..c8a23c84 --- /dev/null +++ b/.github/workflows/honkit-depoly.yml @@ -0,0 +1,47 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build Pages + uses: FriesI23/honkit-build-page@v2 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: "_site" + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/05.installation.md b/05.installation.md index 4a2111d7..9e8ab287 100644 --- a/05.installation.md +++ b/05.installation.md @@ -1,11 +1,10 @@ -# 安装 {#installation} +# 安装 -我们在本书中提及“Python 3”时,我们指的是任何大于等于 [ {{ book.pythonVersion }}](https://www.python.org/downloads/) 的 Python 发行版。[^1] +我们在本书中提及“Python 3”时,我们指的是任何大于等于 3.5.1 的 Python 发行版。 ## 在 Windows 中安装 -访问 https://www.python.org/downloads/ 并下载最新版本的 Python。在本书撰写的时点,最新版本为 Python 3.5.1。 -其安装过程与其它 Windows 平台的软件的安装过程无异。 +访问 https://www.python.org/downloads/ 并下载最新版本的 Python。在本书撰写的时点,最新版本为 Python 3.5.1。 其安装过程与其它 Windows 平台的软件的安装过程无异。 注意:请务必确认你勾选了 `Add Python 3.5 to PATH` 选项。 @@ -17,37 +16,36 @@ 如果你的环境变量(Path)未正确设置,可以遵循上述步骤予以修正。否则,请参阅 `在 Windows 中运行 Python 提示符` 。 -注意:对于那些对编程有所了解的人,如果你熟悉 Docker,可以参阅 [Python in Docker](https://hub.docker.com/_/python/) 和 [Docker on Windows](https://docs.docker.com/windows/)。 +注意:对于那些对编程有所了解的人,如果你熟悉 Docker,可以参阅 [Python in Docker](https://hub.docker.com/\_/python/) 和 [Docker on Windows](https://docs.docker.com/windows/)。 -### DOS 提示符 {#dos-prompt} +### DOS 提示符 如果你希望在 Windows 命令行使用 Python,比如 DOS 提示符,你需要设置相应的PATH 环境变量。 对于 Windows 2000,XP,2003,点击 `控制面板` → `系统` → `高级` → `环境变量` 。点击 _系统变量_ 选项卡下名为 `PATH` 的变量,选择 `编辑` 并在已存在的字符串末尾添加 `;C:\Python35` (请确保该文件夹确实存在,对于更新版本的 Python 文件夹的名字可能有所不同)。当然,你应该使用恰当的目录名称。 - 对于更古老的 Windows 系统版本,打开文件 `C:\AUTOEXEC.BAT` 并在其中添加一行 `PATH=%PATH%;C:\Python35` ,完成编辑并保存后,你需要重启系统。对于 Windows NT 系统,则对应 `AUTOEXEC.NT` 文件。 对于 Windows Vista: -- 点击开始并选择 `控制面板` 。 -- 点击系统,在右侧你将会看见“浏览你的计算机的基本信息” -- 左侧将由一个任务列表,最后一个即为“高级系统设置”,点击它。 -- `高级` 标签栏下可以看见 `系统属性` 对话框。 -- 在较低位置的名为 `系统变量` 的对话框,向下滚动至变量部分并点击 `编辑` 按钮。 -- 修改你需要改动的变量。 -- 重启系统。Vista 直至重启前都不会应用系统变量环境的改动。 +* 点击开始并选择 `控制面板` 。 +* 点击系统,在右侧你将会看见“浏览你的计算机的基本信息” +* 左侧将由一个任务列表,最后一个即为“高级系统设置”,点击它。 +* `高级` 标签栏下可以看见 `系统属性` 对话框。 +* 在较低位置的名为 `系统变量` 的对话框,向下滚动至变量部分并点击 `编辑` 按钮。 +* 修改你需要改动的变量。 +* 重启系统。Vista 直至重启前都不会应用系统变量环境的改动。 对于 Windos 7 与 8: -- 在桌面右击计算机并选择 `属性` 或点击 `开始` 并选择 `控制面板` → `系统与安全` → `系统` 。点击左侧的 `高级系统设置` 并选择 `高级` 标签。点击底部 `系统变量` 下的 `环境变量` ,找到 `PATH` 属性,将其选中并点击 `编辑` 。 -- 前往变量值的最后一行并添加 `;C:\Python35` (请确保该文件夹确实存在,对于更新版本的 Python 文件夹的名字可能有所不同)至业已存在的部分的后方。当然,你应该使用恰当的目录名称。 -- 如果该变量值为 `%SystemRoot%\system32;` 则在修改后其应变为 `%SystemRoot%\system32;C:\Python35` 。 -- 点击 `确定` 以完成操作。你不需要进行重启,不过你可能需要关闭并重启命令提示符。 +* 在桌面右击计算机并选择 `属性` 或点击 `开始` 并选择 `控制面板` → `系统与安全` → `系统` 。点击左侧的 `高级系统设置` 并选择 `高级` 标签。点击底部 `系统变量` 下的 `环境变量` ,找到 `PATH` 属性,将其选中并点击 `编辑` 。 +* 前往变量值的最后一行并添加 `;C:\Python35` (请确保该文件夹确实存在,对于更新版本的 Python 文件夹的名字可能有所不同)至业已存在的部分的后方。当然,你应该使用恰当的目录名称。 +* 如果该变量值为 `%SystemRoot%\system32;` 则在修改后其应变为 `%SystemRoot%\system32;C:\Python35` 。 +* 点击 `确定` 以完成操作。你不需要进行重启,不过你可能需要关闭并重启命令提示符。 ### 在 Windows 下运行 Python 命令提示符 -对于 Windows 用户来说,如果你已经正确并恰当地[设置了 `PATH ` 变量](#dos-prompt),你可以在命令行中运行解释程序。 +对于 Windows 用户来说,如果你已经正确并恰当地[设置了 `PATH` 变量](05.installation.md#dos-prompt),你可以在命令行中运行解释程序。 要想在 Windows 中运行终端,点击开始并点击 `运行` 。在对话中输入 `cmd` 并按下回车键。 @@ -67,7 +65,6 @@ 你会看到在运行命令后 Python 的版本信息显示在屏幕上: - ``` $ python3 -V Python 3.5.1 @@ -83,6 +80,4 @@ Python 3.5.1 接下来,我们将要撰写我们的第一个 Python 程序。 ---- - -[^1]: 本书采用 Python 3.5.1 用来讲授,但最新版本 Python 已非此版本。在翻译时遵从原书内容继续沿用 Python 3.5.1 版本,请读者自行代换为最新版本。 +*** diff --git a/06.first_steps.md b/06.first_steps.md index 9a2c167d..285f0931 100644 --- a/06.first_steps.md +++ b/06.first_steps.md @@ -6,7 +6,7 @@ ## 使用解释器提示符 -在你的操作系统中打开终端(Terminal)程序(正如我们先前在 [安装](./installation.md#installation) 章节所讨论过的那样)然后通过输入 `python3` 并按下 `[enter]` 键来打开 Python 提示符(Python Prompt)。 +在你的操作系统中打开终端(Terminal)程序(正如我们先前在 [安装](05.installation.md) 章节所讨论过的那样)然后通过输入 `python3` 并按下 `[enter]` 键来打开 Python 提示符(Python Prompt)。 当你启动 Python 后,你会看见在你能开始输入内容的地方出现了 `>>>` 。这个被称作 _Python 解释器提示符(Python Interpreter Prompt)_ 。 @@ -20,7 +20,6 @@ print("Hello World") 下面是一个在 Mac OS X 电脑上你能够看见的结果的示例。有关 Python 软件的细节将会因为你使用的电脑而有所不同,但是从提示符(如 `>>>` )开始部分应该是相同的,而不会受到操作系统的影响。 - ```python > python3 Python 3.5.1 (default, Jan 14 2016, 06:54:11) @@ -50,56 +49,55 @@ Hello World 如果你正在使用 Windows 系统,**不要用记事本**——这是一个很糟糕的选择,因为它没有语法加亮功能,同样重要的另一个原因是,它不支持文本缩进功能,这一功能我们之后将会了解它究竟有多重要。而一款好的编辑器能够自动帮你完成这一工作。 -如果你已是一名经验丰富的程序员,那你一定在用 [Vim](http://www.vim.org) 或 [Emacs](http://www.gnu.org/software/emacs/) 了。无需多言,它们都是最强大的编辑器之一,用它们来编写你的 Python 程序自是受益颇多。我个人用它们来编写了我大部分程序,同时也因此写了一本书《[Entire Book on Vim]({{ book.vimBookUrl }})》。 +如果你已是一名经验丰富的程序员,那你一定在用 [Vim](http://www.vim.org) 或 [Emacs](http://www.gnu.org/software/emacs/) 了。无需多言,它们都是最强大的编辑器之一,用它们来编写你的 Python 程序自是受益颇多。我个人用它们来编写了我大部分程序,同时也因此写了一本书《\[Entire Book on Vim]\(\{{ book.vimBookUrl \}})》。 或许你有意去花费时间来学习 Vim 或 Emacs,那么我自是强烈推荐你学习它们二者中的一款,它们将在长远意义上对你裨益颇深。当然,正如我先前所推荐的,初学者可以以 PyCharm 开始,从而在此刻专注于学习 Python 而不是编辑器。 再此重申,请选择一款合适的编辑器——它能够让编写 Python 程序变得更加有趣且容易。 -## PyCharm {#pycharm} +## PyCharm [PyCharm 教育版](https://www.jetbrains.com/pycharm-edu/)是一款能够对你编写 Python 程序的工作有所帮助的免费编辑器。 当你打开 PyCharm 时,你会看见如下界面,点击 `Create New Project` : -![当你打开 PyCharm 时](./img/pycharm_open.png) +![当你打开 PyCharm 时](img/pycharm\_open.png) 选择 `Pure Python` : -![PyCharm 新项目](./img/pycharm_create_new_project.png) +![PyCharm 新项目](img/pycharm\_create\_new\_project.png) 将你的项目路径位置中的 `untitled` 更改为 `helloworld` ,你所看到的界面细节应该类似于下方这番: -![PyCharm 项目细节](./img/pycharm_create_new_project_pure_python.png) +![PyCharm 项目细节](img/pycharm\_create\_new\_project\_pure\_python.png) 点击 `Create` 按钮。 对侧边栏中的 `helloworld` 右击选中,并选择 `New` -> `Python File` : -![PyCharm -> New -> Python File](./img/pycharm_new_python_file.png) +![PyCharm -> New -> Python File](img/pycharm\_new\_python\_file.png) 你会被要求输入名字,现在输入 `hello` : -![PyCharm 新文件对话框](./img/pycharm_new_file_input.png) +![PyCharm 新文件对话框](img/pycharm\_new\_file\_input.png) 现在你便可以看见一个新的文件已为你开启: -![PyCharm hello.py 文件](./img/pycharm_hello_open.png) +![PyCharm hello.py 文件](img/pycharm\_hello\_open.png) 删除那些已存在的内容,现在由你自己输入以下代码: - - ```python print("hello world") ``` + 现在右击你所输入的内容(无需选中文本),然后点击 `Run 'hello'` 。 -![PyCharm Run 'hello'](./img/pycharm_run.png) +![PyCharm Run 'hello'](img/pycharm\_run.png) 此刻你将会看到你的程序所输出的内容(它所打印出来的内容): -![PyCharm 输出内容](./img/pycharm_output.png) +![PyCharm 输出内容](img/pycharm\_output.png) 嚯!虽然只是刚开始的几个步骤,但从今以后,每当我们要求你创建一个新的文件时,记住只需在 `helloworld` 上右击并选择 -> `New` -> `Python File` 并继续如上所述步骤一般输入内容并运行即可。 @@ -108,27 +106,27 @@ print("hello world") ## Vim 1. 安装 [Vim](http://www.vim.org)。 - * Mac OS X 应该通过 [HomeBrew](http://brew.sh/) 来安装 `macvim` 包。 - * Windows 用户应该通过 [Vim 官方网站](http://www.vim.org/download.php) 下载“自安装可执行文件”。 - * GNU/Linux 用户应该通过他们使用的发行版的软件仓库获取 Vim。例如 Debian 与 Ubuntu 用户可以安装 `vim` 包。 + * Mac OS X 应该通过 [HomeBrew](http://brew.sh/) 来安装 `macvim` 包。 + * Windows 用户应该通过 [Vim 官方网站](http://www.vim.org/download.php) 下载“自安装可执行文件”。 + * GNU/Linux 用户应该通过他们使用的发行版的软件仓库获取 Vim。例如 Debian 与 Ubuntu 用户可以安装 `vim` 包。 2. 安装 [jedi-vim](https://github.com/davidhalter/jedi-vim) 插件为 Vim 增添自动完成功能。 3. 安装与之相应的 `jedi` Python 包:`pip install -U jedi` ## Emacs 1. 安装 [Emacs 24+](http://www.gnu.org/software/emacs/)。 - * Mac OS X 用户应该从 http://emacsformacosx.com 获取 Emacs。 - * Windows 用户应该从 http://ftp.gnu.org/gnu/emacs/windows/ 获取 Emacs。 - * GNU/Linux 用户应该从他们使用的发行版的软件仓库获取 Emacs。如 Debian 和 Ubuntu 用户可以安装 `emacs24` 包。 + * Mac OS X 用户应该从 http://emacsformacosx.com 获取 Emacs。 + * Windows 用户应该从 http://ftp.gnu.org/gnu/emacs/windows/ 获取 Emacs。 + * GNU/Linux 用户应该从他们使用的发行版的软件仓库获取 Emacs。如 Debian 和 Ubuntu 用户可以安装 `emacs24` 包。 2. 安装 [ELPY](https://github.com/jorgenschaefer/elpy/wiki)。 ## 使用一份源代码文件 -现在让我们回到编程中来。在你学习一门新的编程语言时有一项传统,你所编写并运行的第一个程序应该是 “Hello World” 程序——它所做的全部工作便是宣言你所运行的“Hello World”这句话。正如西蒙·科泽斯(Simon Cozens,一译西蒙·寇森斯) [^1] 所说,这是“向编程之神所称颂的传统咒语,愿他帮助并保佑你更好的学习这门语言”。 +现在让我们回到编程中来。在你学习一门新的编程语言时有一项传统,你所编写并运行的第一个程序应该是 “Hello World” 程序——它所做的全部工作便是宣言你所运行的“Hello World”这句话。正如西蒙·科泽斯(Simon Cozens,一译西蒙·寇森斯) 所说,这是“向编程之神所称颂的传统咒语,愿他帮助并保佑你更好的学习这门语言”。 启动你所选择的编辑器,输入如下程序并将它保存为 `hello.py` 。 -如果你正在使用 PyCharm,我们已经讨论过[如何从源文件中运行它了](#pycharm)。 +如果你正在使用 PyCharm,我们已经讨论过[如何从源文件中运行它了](06.first\_steps.md#pycharm)。 对于其它编辑器,打开一个新文件名将其命名为 `hello.py` ,然后输入如下内容: @@ -138,9 +136,9 @@ print("hello world") 你应当将文件保存到哪里?保存到任何你知道其位置与路径的文件夹。如果你不了解这句话是什么意思,那就创建一个新文件夹并用这一路径来保存并运行你所有的 Python 程序: -- Mac OS X 上的 `/tmp/py` 。 -- GNU/Linux 上的 `/tmp/py` 。 -- Windows 上的 `C:\\py` 。 +* Mac OS X 上的 `/tmp/py` 。 +* GNU/Linux 上的 `/tmp/py` 。 +* Windows 上的 `C:\\py` 。 要想创建上述文件夹(在你正在使用的操作系统上),你可以在终端上使用 `mkdir` 命令,如 `mkdir /tmp/py` 。 @@ -148,7 +146,7 @@ print("hello world") 要想运行你的 Python 程序: -1. 打开终端窗口(你可查阅先前的 [安装](./installation.md#installation)章节来了解应该怎么做)。 +1. 打开终端窗口(你可查阅先前的 [安装](05.installation.md)章节来了解应该怎么做)。 2. 使用 `cd` 命令来**改**变**目**录到你保存文件的地方,例如 `cd /tmp/py` 。 3. 通过输入命令 `python hello.py` 来运行程序。程序的输出结果应如下方所示: @@ -157,11 +155,11 @@ $ python hello.py hello world ``` -![在终端中运行程序的截图](./img/terminal_screenshot.png) +![在终端中运行程序的截图](img/terminal\_screenshot.png) 如果你得到了与上图类似的输出结果,那么恭喜你!——你已经成功运行了你的第一个 Python 程序。你亦已经成功穿过了学习编程的最困难的部分,也就是,开始编写你的第一个程序! -如果你遭遇了什么错误,请确认是否已经_正确地_输入了上面所列出的内容,并尝试重新运行程序。要注意 Python 是区分大小写的,如 `print` 和 `Print` 是不同的——注意前者的 p 是小写的,而后者的 P 是大写的。此外,你需要确保每一行的第一个字符前面都没有任何空格或制表格——我们会在后面了解 [为什么这件事如此重要](./07.basics.md#indentation)。 +如果你遭遇了什么错误,请确认是否已经\_正确地\_输入了上面所列出的内容,并尝试重新运行程序。要注意 Python 是区分大小写的,如 `print` 和 `Print` 是不同的——注意前者的 p 是小写的,而后者的 P 是大写的。此外,你需要确保每一行的第一个字符前面都没有任何空格或制表格——我们会在后面了解 [为什么这件事如此重要](07.basics.md#indentation)。 **它是如何工作的** @@ -183,6 +181,4 @@ hello world 从此你便成为一名 Python 用户了,现在让我们来学习更多有关 Python 的概念。 ---- - -[^1]: 令人印象深刻的《Beginning Perl》一书的作者。——原书注。在本书中,除特别说明的注释外,其余注释均为译者所加。 +*** diff --git a/09.control_flow.md b/09.control_flow.md index c8d3a8b8..b847a82d 100644 --- a/09.control_flow.md +++ b/09.control_flow.md @@ -1,4 +1,4 @@ -# 控制流 {#control-flow} +# 控制流 截止到现在,在我们所看过的程序中,总是有一系列语句从上到下精确排列,并交由 Python 忠实地执行。如果你想改变这一工作流程,应该怎么做?就像这样的情况:你需要程序作出一些决定,并依据不同的情况去完成不同的事情,例如依据每天时间的不同打印出 '早上好' 'Good Morning' 或 '晚上好' 'Good Evening'? @@ -6,20 +6,56 @@ ## `if` 语句 -`if` 语句用以检查条件:*如果* 条件为真(True),我们将运行一块语句(称作 _if-block_ 或 _if 块_),*否则* 我们将运行另一块语句(称作 _else-block_ 或 _else 块_)。其中 *else* 从句是可选的。 +`if` 语句用以检查条件:_如果_ 条件为真(True),我们将运行一块语句(称作 _if-block_ 或 _if 块_),_否则_ 我们将运行另一块语句(称作 _else-block_ 或 _else 块_)。其中 _else_ 从句是可选的。 案例(保存为 `if.py`): - -
{% include "./programs/if.py" %}
+```python +number = 23 +guess = int(input('Enter an integer : ')) + +if guess == number: + # 新块从这里开始 + print('Congratulations, you guessed it.') + print('(but you do not win any prizes!)') + # 新块在这里结束 +elif guess < number: + # 另一代码块 + print('No, it is a little higher than that') + # 你可以在此做任何你希望在该代码块内进行的事情 +else: + print('No, it is a little lower than that') + # 你必须通过猜测一个大于(>)设置数的数字来到达这里。 + +print('Done') +# 这最后一句语句将在 +# if 语句执行完毕后执行。 + +``` 输出: -
{% include "./programs/if.txt" %}
+```python +$ python if.py +Enter an integer : 50 +No, it is a little lower than that +Done + +$ python if.py +Enter an integer : 22 +No, it is a little higher than that +Done + +$ python if.py +Enter an integer : 23 +Congratulations, you guessed it. +(but you do not win any prizes!) +Done +``` **它是如何工作的** -在这个程序中,我们根据用户猜测的数字来检查这一数字是否是我们所设置的。我们将变量 `number` 设为任何我们所希望的整数,例如 `23`。然后,我们通过 `input()` 函数来获取用户的猜测数。所谓函数是一种可重复使用的程序。我们将在[下一章](./10.functions.md#functions)详细讨论它。 +在这个程序中,我们根据用户猜测的数字来检查这一数字是否是我们所设置的。我们将变量 `number` 设为任何我们所希望的整数,例如 `23`。然后,我们通过 `input()` 函数来获取用户的猜测数。所谓函数是一种可重复使用的程序。我们将在[下一章](10.functions.md#functions)详细讨论它。 我们为内置的 `input` 函数提供一串打印到屏幕上的字符串并等待用户的输入。一旦我们输入了某些内容并按下键盘上的 `enter` 键,`input()` 函数将以字符串的形式返回我们所输入的内容。然后我们通过 `int` 将这个字符串转换成一个整数并将其储存在变量 `guess` 中。实际上,`int` 是一个类(Class),但你现在你所需要知道的就是你可以使用它将一串字符串转换成一个整数(假设这个字符串的文本中含有一个有效的整数)。 @@ -45,85 +81,148 @@ if True: 尽管这是一个非常简单的程序,我也一直在其中指出你应该注意的事情。所有的这些都可算是简单易懂(对于那些具有 C/C++ 背景的人来说是相当简单易懂)。不过在开始时它们还是可能会不断吸引你的注意,不断地去在意它们。但经过一些更丰富的操作后你就会习惯它们及其中的逻辑,它们对于你来说将会成为“自然而然”的事情。 > **针对 C/C++ 程序员的提示** -> -> Python 中不存在 `switch` 语句。你可以通过使用 `if..elif..else` 语句来实现同样的事情(在某些情况下,使用一部[字典](./12.data_structures.md#dictionary)能够更快速地完成)。 +> +> Python 中不存在 `switch` 语句。你可以通过使用 `if..elif..else` 语句来实现同样的事情(在某些情况下,使用一部[字典](12.data\_structures.md#dictionary)能够更快速地完成)。 ## `while` 语句 -`while` 语句能够让你在条件为真的前提下重复执行某块语句。 `while` 语句是 *循环(Looping)* 语句的一种。`while` 语句同样可以拥有 `else` 子句作为可选选项。 +`while` 语句能够让你在条件为真的前提下重复执行某块语句。 `while` 语句是 _循环(Looping)_ 语句的一种。`while` 语句同样可以拥有 `else` 子句作为可选选项。 案例(保存为 `while.py`): -
{% include "./programs/while.py" %}
+```python +number = 23 +running = True + +while running: + guess = int(input('Enter an integer : ')) + + if guess == number: + print('Congratulations, you guessed it.') + # 这将导致 while 循环中止 + running = False + elif guess < number: + print('No, it is a little higher than that.') + else: + print('No, it is a little lower than that.') +else: + print('The while loop is over.') + # 在这里你可以做你想做的任何事 + +print('Done') +``` 输出: -
{% include "./programs/while.txt" %}
+```python +$ python while.py +Enter an integer : 50 +No, it is a little lower than that. +Enter an integer : 22 +No, it is a little higher than that. +Enter an integer : 23 +Congratulations, you guessed it. +The while loop is over. +Done +``` **它是如何工作的** 在这一程序中,我们依旧通过猜数游戏来演示,不过新程序的优点在于能够允许用户持续猜测直至他猜中为止——而无需像我们在上一节中所做的那样,每次猜测都要重新运行程序。这种变化恰到好处地演示了 `while` 语句的作用。 -首先我们将 `input` 与 `if` 语句移到 `while` 循环之中,并在 while 循环开始前将变量 `running` 设置为 `True`。程序开始时,我们首先检查变量 `running` 是否为 `True`,之后再执行相应的 *while 块*。在这一代码块被执行之后,将会重新对条件进行检查,在本例中也就是 `running` 变量。如果它依旧为 `True`,我们将再次执行 while 块,否则我们将继续执行可选的 else 块,然后进入到下一个语句中。 +首先我们将 `input` 与 `if` 语句移到 `while` 循环之中,并在 while 循环开始前将变量 `running` 设置为 `True`。程序开始时,我们首先检查变量 `running` 是否为 `True`,之后再执行相应的 _while 块_。在这一代码块被执行之后,将会重新对条件进行检查,在本例中也就是 `running` 变量。如果它依旧为 `True`,我们将再次执行 while 块,否则我们将继续执行可选的 else 块,然后进入到下一个语句中。 `else` 代码块在 `while` 循环的条件变为 `False` 时开始执行——这个开始的时机甚至可能是在第一次检查条件的时候。如果 `while` 循环中存在一个 `else` 代码块,它将总是被执行,除非你通过 `break` 语句来中断这一循环。 `True` 和 `False` 被称作布尔(Boolean)型,你可以将它们分别等价地视为 `1` 与 `0`。 > **针对 C/C++ 程序员的提示** -> +> > 你可以在 `while` 循环中使用 `else` 从句。 - ## `for` 循环 -`for...in` 语句是另一种循环语句,其特点是会在一系列对象上进行*迭代(Iterates)*,意即它会遍历序列中的每一个项目。我们将在后面的[序列(Sequences)](./12.data_structures.md#sequence)章节中了解有关它的更多内容。现在你所需要的就是所谓队列就是一系列项目的有序集合。 +`for...in` 语句是另一种循环语句,其特点是会在一系列对象上进行_迭代(Iterates)_,意即它会遍历序列中的每一个项目。我们将在后面的[序列(Sequences)](12.data\_structures.md#sequence)章节中了解有关它的更多内容。现在你所需要的就是所谓队列就是一系列项目的有序集合。 案例(保存为 `for.py`): -
{% include "./programs/for.py" %}
+```python +for i in range(1, 5): + print(i) +else: + print('The for loop is over') + +``` 输出: -
{% include "./programs/for.txt" %}
+```python +$ python for.py +1 +2 +3 +4 +The for loop is over +``` **它是如何工作的** -在这一程序中,我们打印了一个数字*序列*。我们通过内置的 `range` 函数生成这一数字序列。 +在这一程序中,我们打印了一个数字_序列_。我们通过内置的 `range` 函数生成这一数字序列。 -在这里我们所要做的事情是提供两个数字,而 `range` 将会返回一个数字序列,从第一个数字开始,至第二个数字结束。举个例子,`range(1,5)` 将输出序列 `[1, 2, 3, 4]`。在默认情况下,`range` 将会以 1 逐步递增。如果我们向 `range` 提供第三个数字,则这个数字将成为逐步递增的加数。同样举个例子来说明,`range(1,5,2)` 将会输出 `[1, 3]`。要记住这一序列扩展*直到*第二个数字,也就是说,它*不会*包括第二个数字在内。 +在这里我们所要做的事情是提供两个数字,而 `range` 将会返回一个数字序列,从第一个数字开始,至第二个数字结束。举个例子,`range(1,5)` 将输出序列 `[1, 2, 3, 4]`。在默认情况下,`range` 将会以 1 逐步递增。如果我们向 `range` 提供第三个数字,则这个数字将成为逐步递增的加数。同样举个例子来说明,`range(1,5,2)` 将会输出 `[1, 3]`。要记住这一序列扩展_直到_第二个数字,也就是说,它_不会_包括第二个数字在内。 -另外需要注意的是,`range()` 每次只会生成一个数字,如果你希望获得完整的数字列表,要在使用 `range()` 时调用 `list()`。例如下面这样:`list(range(5))` ,它将会返回 `[0, 1, 2, 3, 4]`。有关列表的详细解释将会在 [《数据结构》一章](./12.data_structures.md#data-structures)呈现。 +另外需要注意的是,`range()` 每次只会生成一个数字,如果你希望获得完整的数字列表,要在使用 `range()` 时调用 `list()`。例如下面这样:`list(range(5))` ,它将会返回 `[0, 1, 2, 3, 4]`。有关列表的详细解释将会在 [《数据结构》一章](12.data\_structures.md#data-structures)呈现。 然后 `for` 循环就会在这一范围内展开递归——`for i in range(1,5)` 等价于 `for i in [1, 2, 3, 4]`,这个操作将依次将队列里的每个数字(或是对象)分配给 `i`,一次一个,然后以每个 `i` 的值执行语句块。在本例中,我们这一语句块所做的就是打印出这些值。 -同样要记住,`else` 部分是可选的。当循环中包含他时,它总会在 `for` 循环结束后开始执行,除非程序遇到了 [break](#break-statement) 语句。 +同样要记住,`else` 部分是可选的。当循环中包含他时,它总会在 `for` 循环结束后开始执行,除非程序遇到了 [break](09.control\_flow.md#break-statement) 语句。 另一个需要注意的地方是 `for...in` 能在任何队列中工作。在这里,我们有的是通过内置的 `range` 函数生成的一串数字列表,但总体来说我们可以包含任何类型对象的队列!我们将会在后面的章节详细解释这一观念。 > **针对 C/C++/Java/C# 程序员的提示** -> +> > Python 中的 `for` 循环和 C/C++ 中的 `for` 循环可以说是完全不同。C# 程序员会注意到 Python 中的 `for` 循环与 C# 中的 `foreach` 循环相似。Java 程序员则会注意到它同样与 Java 1.5 中的 `for (int i : IntArray)` 无甚区别。 -> +> > 在 C/C++ 中,如果你希望编写 `for (int i = 0; i < 5; i++)`,那么在 Python 你只需要写下 `for i in range(0,5)`。正如你所看到的,Python 中的 `for` 循环将更加简单,更具表现力且更不容易出错。 -## `break` 语句 {#break-statement} +## `break` 语句 -`break` 语句用以*中断*(Break)循环语句,也就是中止循环语句的执行,即使循环条件没有变更为 `False`,或队列中的项目尚未完全迭代依旧如此。 +`break` 语句用以_中断_(Break)循环语句,也就是中止循环语句的执行,即使循环条件没有变更为 `False`,或队列中的项目尚未完全迭代依旧如此。 -有一点需要尤其注意,如果你 *中断* 了一个 `for` 或 `while` 循环,任何相应循环中的 `else` 块都将*不会*被执行。 +有一点需要尤其注意,如果你 _中断_ 了一个 `for` 或 `while` 循环,任何相应循环中的 `else` 块都将_不会_被执行。 案例(保存为 `break.py`): -
{% include "./programs/break.py" %}
+```python +while True: + s = input('Enter something : ') + if s == 'quit': + break + print('Length of the string is', len(s)) +print('Done') + +``` 输出: -
{% include "./programs/break.txt" %}
+```python +$ python break.py +Enter something : Programming is fun +Length of the string is 18 +Enter something : When the work is done +Length of the string is 21 +Enter something : if you wanna make your work also fun: +Length of the string is 37 +Enter something : use Python! +Length of the string is 11 +Enter something : quit +Done + +``` **它是如何工作的** -在本程序中,我们重复地接受用户的输入内容并打印出每一次输入内容的长度。我们通过检查用户输入的是否是 `quit` 这一特殊条件来判断是否应该终止程序。我们通过*中断*循环并转进至程序末尾来结束这一程序。 +在本程序中,我们重复地接受用户的输入内容并打印出每一次输入内容的长度。我们通过检查用户输入的是否是 `quit` 这一特殊条件来判断是否应该终止程序。我们通过_中断_循环并转进至程序末尾来结束这一程序。 输入字符串的长度可以通过内置的 `len` 函数来找到。 @@ -139,17 +238,39 @@ When the work is done if you wanna make your work also fun: use Python! ``` -## `continue` 语句 {#continue-statement} -`continue` 语句用以告诉 Python 跳过当前循环块中的剩余语句,并*继续*该循环的下一次迭代。 +## `continue` 语句 + +`continue` 语句用以告诉 Python 跳过当前循环块中的剩余语句,并_继续_该循环的下一次迭代。 案例(保存为 `continue.py`): -
{% include "./programs/continue.py" %}
+```python +while True: + s = input('Enter something : ') + if s == 'quit': + break + if len(s) < 3: + print('Too small') + continue + print('Input is of sufficient length') + # 自此处起继续进行其它任何处理 + +``` 输出: -
{% include "./programs/continue.txt" %}
+```python +$ python continue.py +Enter something : a +Too small +Enter something : 12 +Too small +Enter something : abc +Input is of sufficient length +Enter something : quit + +``` **它是如何工作的** diff --git a/10.functions.md b/10.functions.md index 70ecea74..2a40d4ac 100644 --- a/10.functions.md +++ b/10.functions.md @@ -1,18 +1,32 @@ # 函数 -函数(Functions)是指可重复使用的程序片段。它们允许你为某个代码块赋予名字,允许你通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的*调用(Calling)*函数。我们已经使用过了许多内置的函数,例如 `len` 和 `range`。 +函数(Functions)是指可重复使用的程序片段。它们允许你为某个代码块赋予名字,允许你通过这一特殊的名字在你的程序任何地方来运行代码块,并可重复任何次数。这就是所谓的\*调用(Calling)\*函数。我们已经使用过了许多内置的函数,例如 `len` 和 `range`。 -函数概念可能是在任何复杂的软件(无论使用的是何种编程语言)中*最*重要的构建块,所以我们接下来将在本章中探讨有关函数的各个方面。 +函数概念可能是在任何复杂的软件(无论使用的是何种编程语言)中_最_重要的构建块,所以我们接下来将在本章中探讨有关函数的各个方面。 -函数可以通过关键字 `def` 来定义。这一关键字后跟一个函数的*标识符*名称,再跟一对圆括号,其中可以包括一些变量的名称,再以冒号结尾,结束这一行。随后而来的语句块是函数的一部分。下面的案例将会展示出这其实非常简单: +函数可以通过关键字 `def` 来定义。这一关键字后跟一个函数的_标识符_名称,再跟一对圆括号,其中可以包括一些变量的名称,再以冒号结尾,结束这一行。随后而来的语句块是函数的一部分。下面的案例将会展示出这其实非常简单: 案例(保存为 `function1.py`): -
{% include "./programs/function1.py" %}
+```python +def say_hello(): + # 该块属于这一函数 + print('hello world') +# 函数结束 + +say_hello() # 调用函数 +say_hello() # 再次调用函数 + +``` 输出: -
{% include "./programs/function1.txt" %}
+```python +$ python function1.py +hello world +hello world + +``` **它是如何工作的** @@ -20,19 +34,42 @@ 要注意到我们可以两次调用相同的函数,这意味着我们不必重新把代码再写一次。 -## 函数参数[^1] +## 函数参数 -函数可以获取参数,这个参数的值由你所提供,借此,函数便可以利用这些值来*做*一些事情。这些参数与变量类似,这些变量的值在我们调用函数时已被定义,且在函数运行时均已赋值完成。 +函数可以获取参数,这个参数的值由你所提供,借此,函数便可以利用这些值来_做_一些事情。这些参数与变量类似,这些变量的值在我们调用函数时已被定义,且在函数运行时均已赋值完成。 -函数中的参数通过将其放置在用以定义函数的一对圆括号中指定,并通过逗号予以分隔。当我们调用函数时,我们以同样的形式提供需要的值。要注意在此使用的术语——在定义函数时给定的名称称作_“形参”(Parameters)_,在调用函数时你所提供给函数的值称作_“实参”(Arguments)_。 +函数中的参数通过将其放置在用以定义函数的一对圆括号中指定,并通过逗号予以分隔。当我们调用函数时,我们以同样的形式提供需要的值。要注意在此使用的术语——在定义函数时给定的名称称作\_“形参”(Parameters)_,在调用函数时你所提供给函数的值称作_“实参”(Arguments)\_。 案例(保存为 `function_param.py`): -
{% include "./programs/function_param.py" %}
+```python +def print_max(a, b): + if a > b: + print(a, 'is maximum') + elif a == b: + print(a, 'is equal to', b) + else: + print(b, 'is maximum') + +# 直接传递字面值 +print_max(3, 4) + +x = 5 +y = 7 + +# 以参数的形式传递变量 +print_max(x, y) + +``` 输出: -
{% include "./programs/function_param.txt" %}
+```python +$ python function_param.py +4 is maximum +7 is maximum + +``` **它是如何工作的** @@ -40,17 +77,36 @@ 第一次调用函数 `print_max` 时,我们以实参的形式直接向函数提供这一数字。在第二次调用时,我们将变量作为实参来调用函数。`print_max(x, y)` 将使得实参 `x` 的值将被赋值给形参 `a`,而实参 `y` 的值将被赋值给形参 `b`。在两次调用中,`print_max` 都以相同的方式工作。 -## 局部变量[^2] +## 局部变量 -当你在一个函数的定义中声明变量时,它们不会以任何方式与身处函数之外但具有相同名称的变量产生关系,也就是说,这些变量名只存在于函数这一*局部(Local)*。这被称为变量的*作用域(Scope)*。所有变量的作用域是它们被定义的块,从定义它们的名字的定义点开始。 +当你在一个函数的定义中声明变量时,它们不会以任何方式与身处函数之外但具有相同名称的变量产生关系,也就是说,这些变量名只存在于函数这一_局部(Local)_。这被称为变量的_作用域(Scope)_。所有变量的作用域是它们被定义的块,从定义它们的名字的定义点开始。 案例(保存为 `function_local.py`): -
{% include "./programs/function_local.py" %}
+```python +x = 50 + + +def func(x): + print('x is', x) + x = 2 + print('Changed local x to', x) + + +func(x) +print('x is still', x) + +``` 输出: -
{% include "./programs/function_local.txt" %}
+```python +$ python function_local.py +x is 50 +Changed local x to 2 +x is still 50 + +``` **它是如何工作的** @@ -60,19 +116,40 @@ 随着最后一句 `print` 语句,我们展示出主代码块中定义的 `x` 的值,由此确认它实际上不受先前调用的函数中的局部变量的影响。 -## `global` 语句 {#global-statement} +## `global` 语句 -如果你想给一个在程序顶层的变量赋值(也就是说它不存在于任何作用域中,无论是函数还是类),那么你必须告诉 Python 这一变量并非局部的,而是*全局(Global)*的。我们需要通过 `global` 语句来完成这件事。因为在不使用 `global` 语句的情况下,不可能为一个定义于函数之外的变量赋值。 +如果你想给一个在程序顶层的变量赋值(也就是说它不存在于任何作用域中,无论是函数还是类),那么你必须告诉 Python 这一变量并非局部的,而是\*全局(Global)\*的。我们需要通过 `global` 语句来完成这件事。因为在不使用 `global` 语句的情况下,不可能为一个定义于函数之外的变量赋值。 你可以使用定义于函数之外的变量的值(假设函数中没有具有相同名字的变量)。然而,这种方式不会受到鼓励而且应该避免,因为它对于程序的读者来说是含糊不清的,无法弄清楚变量的定义究竟在哪。而通过使用 `global` 语句便可清楚看出这一变量是在最外边的代码块中定义的。 案例(保存为 `function_global.py`): -
{% include "./programs/function_global.py" %}
+```python +x = 50 + + +def func(): + global x + + print('x is', x) + x = 2 + print('Changed global x to', x) + + +func() +print('Value of x is', x) + +``` 输出: -
{% include "./programs/function_global.txt" %}
+```python +$ python function_global.py +x is 50 +Changed global x to 2 +Value of x is 2 + +``` **它是如何工作的** @@ -80,45 +157,71 @@ 你可以在同一句 `global` 语句中指定不止一个的全局变量,例如 `global x, y, z`。 -## 默认参数值 {#default-arguments} +## 默认参数值 -对于一些函数来说,你可能为希望使一些参数*可选*并使用默认的值,以避免用户不想为他们提供值的情况。默认参数值可以有效帮助解决这一情况。你可以通过在函数定义时附加一个赋值运算符(`=`)来为参数指定默认参数值。 +对于一些函数来说,你可能为希望使一些参数_可选_并使用默认的值,以避免用户不想为他们提供值的情况。默认参数值可以有效帮助解决这一情况。你可以通过在函数定义时附加一个赋值运算符(`=`)来为参数指定默认参数值。 要注意到,默认参数值应该是常数。更确切地说,默认参数值应该是不可变的——这将在后面的章节中予以更详细的解释。就目前来说,只要记住就行了。 案例(保存为 `function_default.py`): -
{% include "./programs/function_default.py" %}
+```python +def say(message, times=1): + print(message * times) + +say('Hello') +say('World', 5) + +``` 输出: -
{% include "./programs/function_default.txt" %}
+``` +$ python function_default.py +Hello +WorldWorldWorldWorldWorld + +``` **它是如何工作的** 名为 `say` 的函数用以按照给定的次数打印一串字符串。如果我们没有提供一个数值,则将按照默认设置,只打印一次字符串。我们通过为参数 `times` 指定默认参数值 `1` 来实现这一点。 -在第一次使用 `say` 时,我们只提供字符串因而函数只会将这个字符串打印一次。在第二次使用 `say` 时,我们既提供了字符串,同时也提供了一个参数 `5`,声明我们希望*说(Say)*这个字符串五次。 +在第一次使用 `say` 时,我们只提供字符串因而函数只会将这个字符串打印一次。在第二次使用 `say` 时,我们既提供了字符串,同时也提供了一个参数 `5`,声明我们希望_说(Say)_这个字符串五次。 -> *注意* -> +> _注意_ +> > 只有那些位于参数列表末尾的参数才能被赋予默认参数值,意即在函数的参数列表中拥有默认参数值的参数不能位于没有默认参数值的参数之前。 -> -> 这是因为值是按参数所处的位置依次分配的。举例来说,`def func(a, b=5)` 是有效的,但 `def func(a=5, b)` 是*无效的*。 +> +> 这是因为值是按参数所处的位置依次分配的。举例来说,`def func(a, b=5)` 是有效的,但 `def func(a=5, b)` 是_无效的_。 -## 关键字参数[^3] +## 关键字参数 -如果你有一些具有许多参数的函数,而你又希望只对其中的一些进行指定,那么你可以通过命名它们来给这些参数赋值——这就是*关键字参数(Keyword Arguments)*——我们使用命名(关键字)而非位置(一直以来我们所使用的方式)来指定函数中的参数。 +如果你有一些具有许多参数的函数,而你又希望只对其中的一些进行指定,那么你可以通过命名它们来给这些参数赋值——这就是_关键字参数(Keyword Arguments)_——我们使用命名(关键字)而非位置(一直以来我们所使用的方式)来指定函数中的参数。 这样做有两大优点——其一,我们不再需要考虑参数的顺序,函数的使用将更加容易。其二,我们可以只对那些我们希望赋予的参数以赋值,只要其它的参数都具有默认参数值。 案例(保存为 `function_keyword.py`): -
{% include "./programs/function_keyword.py" %}
+```python +def func(a, b=5, c=10): + print('a is', a, 'and b is', b, 'and c is', c) + +func(3, 7) +func(25, c=24) +func(c=50, a=100) + +``` 输出: -
{% include "./programs/function_keyword.txt" %}
+```python +$ python function_keyword.py +a is 3 and b is 7 and c is 10 +a is 25 and b is 5 and c is 24 +a is 100 and b is 5 and c is 50 + +``` **它是如何工作的** @@ -130,15 +233,40 @@ 在第三次调用函数时,`func(c=50, a=100)`,我们全部使用关键字参数来指定值。在这里要注意到,尽管 `a` 在 `c` 之前定义,但我们还是在变量 `a` 之前指定了变量 `c`。 -## 可变参数[^4] +## 可变参数 -有时你可能想定义的函数里面能够有_任意_数量的变量,也就是参数数量是可变的,这可以通过使用星号来实现(将下方案例保存为 `function_varargs.py`): +有时你可能想定义的函数里面能够有\_任意\_数量的变量,也就是参数数量是可变的,这可以通过使用星号来实现(将下方案例保存为 `function_varargs.py`): + +```python +def total(a=5, *numbers, **phonebook): + print('a', a) -
{% include "./programs/function_varargs.py" %}
+ #遍历元组中的所有项目 + for single_item in numbers: + print('single_item', single_item) + + #遍历字典中的所有项目 + for first_part, second_part in phonebook.items(): + print(first_part,second_part) + +print(total(10,1,2,3,Jack=1123,John=2231,Inge=1560)) + +``` 输出: -
{% include "./programs/function_varargs.txt" %}
+```python +$ python function_varargs.py +a 10 +single_item 1 +single_item 2 +single_item 3 +Inge 1560 +John 2231 +Jack 1123 +None + +``` **它是如何工作的** @@ -146,24 +274,38 @@ 类似地,当我们声明一个诸如 `**param` 的双星号参数时,从此处开始直至结束的所有关键字参数都将被收集并汇集成一个名为 `param` 的字典(Dictionary)。 +我们将在[后面的章节](data\_structures.md#data-structures)探索有关元组与字典的更多内容。 -我们将在[后面的章节](./data_structures.md#data-structures)探索有关元组与字典的更多内容。 - -## `return` 语句 {#return-statement} +## `return` 语句 -`return` 语句用于从函数中*返回*,也就是中断函数。我们也可以选择在中断函数时从函数中*返回一个值*。 +`return` 语句用于从函数中_返回_,也就是中断函数。我们也可以选择在中断函数时从函数中_返回一个值_。 案例(保存为 `function_return.py`): -
{% include "./programs/function_return.py" %}
+```python +def maximum(x, y): + if x > y: + return x + elif x == y: + return 'The numbers are equal' + else: + return y + +print(maximum(2, 3)) + +``` 输出: -
{% include "./programs/function_return.txt" %}
+``` +$ python function_return.py +3 + +``` -*它是如何工作的* +_它是如何工作的_ -`maximum` 函数将会返回参数中的最大值,在本例中是提供给函数的数值。它使用一套简单的 `if...else` 语句来找到较大的那个值并将其*返回*。 +`maximum` 函数将会返回参数中的最大值,在本例中是提供给函数的数值。它使用一套简单的 `if...else` 语句来找到较大的那个值并将其_返回_。 要注意到如果 `return` 语句没有搭配任何一个值则代表着 `返回 None`。`None` 在 Python 中一个特殊的类型,代表着虚无。举个例子, 它用于指示一个变量没有值,如果有值则它的值便是 `None(虚无)`。 @@ -180,44 +322,59 @@ Python 中的 `pass` 语句用于指示一个没有内容的语句块。 ## DocStrings -Python 有一个甚是优美的功能称作*文档字符串(Documentation Strings)*,在称呼它时通常会使用另一个短一些的名字*docstrings*。DocStrings 是一款你应当使用的重要工具,它能够帮助你更好地记录程序并让其更加易于理解。令人惊叹的是,当程序实际运行时,我们甚至可以通过一个函数来获取文档! +Python 有一个甚是优美的功能称作_文档字符串(Documentation Strings)_,在称呼它时通常会使用另一个短一些的名字_docstrings_。DocStrings 是一款你应当使用的重要工具,它能够帮助你更好地记录程序并让其更加易于理解。令人惊叹的是,当程序实际运行时,我们甚至可以通过一个函数来获取文档! 案例(保存为 `function_docstring.py`): -
{% include "./programs/function_docstring.py" %}
+```python +def print_max(x, y): + '''打印两个数值中的最大数。 -输出: + 这两个数都应该是整数''' + # 如果可能,将其转换至整数类型 + x = int(x) + y = int(y) -
{% include "./programs/function_docstring.txt" %}
+ if x > y: + print(x, 'is maximum') + else: + print(y, 'is maximum') -**它是如何工作的** +print_max(3, 5) +print(print_max.__doc__) -函数的第一行逻辑行中的字符串是该函数的 *文档字符串(DocString)*。这里要注意文档字符串也适用于后面相关章节将提到的[模块(Modules)](./11.modules.md#modules)与[类(Class)](./14.oop.md#oop) 。 +``` -该文档字符串所约定的是一串多行字符串,其中第一行以某一大写字母开始,以句号结束。第二行为空行,后跟的第三行开始是任何详细的解释说明。[^5]在此*强烈建议*你在你所有重要功能的所有文档字符串中都遵循这一约定。 +输出: -我们可以通过使用函数的 `__doc__`(注意其中的*双下划綫*)属性(属于函数的名称)来获取函数 `print_max` 的文档字符串属性。只消记住 Python 将*所有东西*都视为一个对象,这其中自然包括函数。我们将在后面的[类(Class)](./14.oop.md#oop)章节讨论有关对象的更多细节。 +``` +$ python function_docstring.py +5 is maximum +打印两个数值中的最大数。 -如果你曾使用过 Python 的 `help()` 函数,那么你应该已经了解了文档字符串的用途了。它所做的便是获取函数的 `__doc__` 属性并以一种整洁的方式将其呈现给你。你可以在上方的函数中尝试一下——只需在程序中包含 `help(print_max)` 就行了。要记住你可以通过按下 `q` 键来退出 `help`。 + 这两个数都应该是整数 -自动化工具可以以这种方式检索你的程序中的文档。因此,我*强烈推荐*你为你编写的所有重要的函数配以文档字符串。你的 Python 发行版中附带的 `pydoc` 命令与 `help()` 使用文档字符串的方式类似。 +``` -## 总结 +**它是如何工作的** -我们已经了解了许多方面的函数,但我们依旧还未覆盖到所有类型的函数。不过,我们已经覆盖到了大部分你每天日常使用都会使用到的 Python 函数。 +函数的第一行逻辑行中的字符串是该函数的 _文档字符串(DocString)_。这里要注意文档字符串也适用于后面相关章节将提到的[模块(Modules)](11.modules.md#modules)与[类(Class)](14.oop.md#oop) 。 -接下来,我们将了解如何创建并使用 Python 模块。 +该文档字符串所约定的是一串多行字符串,其中第一行以某一大写字母开始,以句号结束。第二行为空行,后跟的第三行开始是任何详细的解释说明。在此_强烈建议_你在你所有重要功能的所有文档字符串中都遵循这一约定。 +我们可以通过使用函数的 `__doc__`(注意其中的_双下划綫_)属性(属于函数的名称)来获取函数 `print_max` 的文档字符串属性。只消记住 Python 将_所有东西_都视为一个对象,这其中自然包括函数。我们将在后面的[类(Class)](14.oop.md#oop)章节讨论有关对象的更多细节。 +如果你曾使用过 Python 的 `help()` 函数,那么你应该已经了解了文档字符串的用途了。它所做的便是获取函数的 `__doc__` 属性并以一种整洁的方式将其呈现给你。你可以在上方的函数中尝试一下——只需在程序中包含 `help(print_max)` 就行了。要记住你可以通过按下 `q` 键来退出 `help`。 -**** +自动化工具可以以这种方式检索你的程序中的文档。因此,我_强烈推荐_你为你编写的所有重要的函数配以文档字符串。你的 Python 发行版中附带的 `pydoc` 命令与 `help()` 使用文档字符串的方式类似。 -[^1]: 原文作 Function Parameters,沈洁元译本译作“函数形参”。Parameter 和 Argument 同时具有“参数”和“形参”或“实参”两种译法。一般来说,只有在存在形参实参二义关系时,才会特别翻译成“形参”或“实参”。故此节标题 Parameter 作“参数”解。 +## 总结 -[^2]: 原文作 Local Varibles。 +我们已经了解了许多方面的函数,但我们依旧还未覆盖到所有类型的函数。不过,我们已经覆盖到了大部分你每天日常使用都会使用到的 Python 函数。 -[^3]: 原文作 Keyword Arguments,沈洁元译本译作“关键参数”。 +接下来,我们将了解如何创建并使用 Python 模块。 -[^4]: 原文作 VarArgs Parameters,VarArgs 来自于英文“可变的”“自变量(一译变元,台译引数,也可以理解成参数)”两个英文单词的结合,即 **Var**iable **Arg**uments。 +*** -[^5]: 此处指的是以英文撰写的文档字符串内容。 +1. 原文作 Local Varibles。 +2. 原文作 VarArgs Parameters,VarArgs 来自于英文“可变的”“自变量(一译变元,台译引数,也可以理解成参数)”两个英文单词的结合,即 **Var**iable **Arg**uments。 diff --git a/11.modules.md b/11.modules.md index 9634c4b9..07a8364b 100644 --- a/11.modules.md +++ b/11.modules.md @@ -6,27 +6,50 @@ 另一种方法是使用撰写 Python 解释器本身的本地语言来编写模块。举例来说,你可以使用 [C 语言](http://docs.python.org/3/extending/)来撰写 Python 模块,并且在编译后,你可以通过标准 Python 解释器在你的 Python 代码中使用它们。 -一个模块可以被其它程序*导入*并运用其功能。我们在使用 Python 标准库的功能时也同样如此。首先,我们要了解如何使用标准库模块。 +一个模块可以被其它程序_导入_并运用其功能。我们在使用 Python 标准库的功能时也同样如此。首先,我们要了解如何使用标准库模块。 案例 (保存为 `module_using_sys.py`): -
{% include "./programs/module_using_sys.py" %}
+```python +import sys + +print('The command line arguments are:') +for i in sys.argv: + print(i) + +print('\n\nThe PYTHONPATH is', sys.path, '\n') + +``` 输出: -
{% include "./programs/module_using_sys.txt" %}
+```python +$ python module_using_sys.py we are arguments +The command line arguments are: +module_using_sys.py +we +are +arguments + + +The PYTHONPATH is ['/tmp/py', +# many entries here, not shown here +'/Library/Python/2.7/site-packages', +'/usr/local/lib/python2.7/site-packages'] + +``` **它是如何工作的** -首先,我们通过 `import` 语句*导入* `sys` 模块。基本上,这句代码将转化为我们告诉 Python 我们希望使用这一模块。`sys` 模块包含了与 Python 解释器及其环境相关的功能,也就是所谓的*系统*功能(*sys*tem)。 +首先,我们通过 `import` 语句_导入_ `sys` 模块。基本上,这句代码将转化为我们告诉 Python 我们希望使用这一模块。`sys` 模块包含了与 Python 解释器及其环境相关的功能,也就是所谓的_系统_功能(_sys_tem)。 当 Python 运行 `import sys` 这一语句时,它会开始寻找 `sys` 模块。在这一案例中,由于其是一个内置模块,因此 Python 知道应该在哪里找到它。 -如果它不是一个已编译好的模块,即用 Python 编写的模块,那么 Python 解释器将从它的 `sys.path` 变量所提供的目录中进行搜索。如果找到了对应模块,则该模块中的语句将在开始运行,并*能够*为你所使用。在这里需要注意的是,初始化工作只需在我们*第一次*导入模块时完成。 +如果它不是一个已编译好的模块,即用 Python 编写的模块,那么 Python 解释器将从它的 `sys.path` 变量所提供的目录中进行搜索。如果找到了对应模块,则该模块中的语句将在开始运行,并_能够_为你所使用。在这里需要注意的是,初始化工作只需在我们_第一次_导入模块时完成。 `sys` 模块中的 `argv` 变量通过使用点号予以指明,也就是 `sys.argv` 这样的形式。它清晰地表明了这一名称是 `sys` 模块的一部分。这一处理方式的另一个优点是这个名称不会与你程序中的其它任何一个 `argv` 变量冲突。 -`sys.argv` 变量是一系列字符串的*列表(List)*(列表将在[后面的章节](./12.data_structures.md#data-structures)予以详细解释)。具体而言,`sys.argv` 包含了*命令行参数(Command Line Arguments)*这一列表,也就是使用命令行传递给你的程序的参数。 +`sys.argv` 变量是一系列字符串的_列表(List)_(列表将在[后面的章节](12.data\_structures.md#data-structures)予以详细解释)。具体而言,`sys.argv` 包含了\*命令行参数(Command Line Arguments)\*这一列表,也就是使用命令行传递给你的程序的参数。 如果你正在使用一款 IDE 来编写并运行这些程序,请在程序菜单中寻找相关指定命令行参数的选项。 @@ -38,17 +61,17 @@ 另外要注意的是当前目录指的是程序启动的目录。你可以通过运行 `import os; print(os.getcwd())` 来查看你的程序目前所处在的目录。 -## 按字节码编译的 .pyc 文件 {#pyc}[^1] +## 按字节码编译的 .pyc 文件 {#pyc} -导入一个模块是一件代价高昂的事情,因此 Python 引入了一些技巧使其能够更快速的完成。其中一种方式便是创建*按字节码编译的(Byte-Compiled)*文件,这一文件以 `.pyc` 为其扩展名,是将 Python 转换成中间形式的文件(还记得[《介绍》](./04.about_python.md#interpreted)一章中介绍的 Python 是如何工作的吗?)。这一 `.pyc` 文件在你下一次从其它不同的程序导入模块时非常有用——它将更加快速,因为导入模块时所需要的一部分处理工作已经完成了。同时,这些按字节码编译的文件是独立于运行平台的。 +导入一个模块是一件代价高昂的事情,因此 Python 引入了一些技巧使其能够更快速的完成。其中一种方式便是创建\*按字节码编译的(Byte-Compiled)\*文件,这一文件以 `.pyc` 为其扩展名,是将 Python 转换成中间形式的文件(还记得[《介绍》](04.about\_python.md#interpreted)一章中介绍的 Python 是如何工作的吗?)。这一 `.pyc` 文件在你下一次从其它不同的程序导入模块时非常有用——它将更加快速,因为导入模块时所需要的一部分处理工作已经完成了。同时,这些按字节码编译的文件是独立于运行平台的。 -注意:这些 `.pyc` 文件通常会创建在与对应的 `.py` 文件所处的目录中。如果 Python 没有相应的权限对这一目录进行写入文件的操作,那么 `.pyc` 文件将_不会_被创建。 +注意:这些 `.pyc` 文件通常会创建在与对应的 `.py` 文件所处的目录中。如果 Python 没有相应的权限对这一目录进行写入文件的操作,那么 `.pyc` 文件将\_不会\_被创建。 -## `from..import` 语句 {#from-import-statement} +## `from..import` 语句 如果你希望直接将 `argv` 变量导入你的程序(为了避免每次都要输入 `sys.`),那么你可以通过使用 `from sys import argv` 语句来实现这一点。 -> **警告:**一般来说,你应该尽量*避免*使用 `from...import` 语句,而去使用 `import` 语句。这是为了避免在你的程序中出现名称冲突,同时也为了使程序更加易读。 +> \*\*警告:\*\*一般来说,你应该尽量_避免_使用 `from...import` 语句,而去使用 `import` 语句。这是为了避免在你的程序中出现名称冲突,同时也为了使程序更加易读。 案例: @@ -57,17 +80,32 @@ from math import sqrt print("Square root of 16 is", sqrt(16)) ``` -## 模块的 `__name__` {#module-name} +## 模块的 `__name__` 每个模块都有一个名称,而模块中的语句可以找到它们所处的模块的名称。这对于确定模块是独立运行的还是被导入进来运行的这一特定目的来说大为有用。正如先前所提到的,当模块第一次被导入时,它所包含的代码将被执行。我们可以通过这一特性来使模块以不同的方式运行,这取决于它是为自己所用还是从其它从的模块中导入而来。这可以通过使用模块的 `__name__` 属性来实现。 案例(保存为 `module_using_name.py`): -
{% include "./programs/module_using_name.py" %}
+```python +if __name__ == '__main__': + print('This program is being run by itself') +else: + print('I am being imported from another module') + +``` 输出: -
{% include "./programs/module_using_name.txt" %}
+```python +$ python module_using_name.py +This program is being run by itself + +$ python +>>> import module_using_name +I am being imported from another module +>>> + +``` **它是如何工作的** @@ -79,19 +117,31 @@ print("Square root of 16 is", sqrt(16)) 案例(保存为 `mymodule.py`): -
{% include "./programs/mymodule.py" %}
+```python +``` -上方所呈现的就是一个简单的*模块*。正如你所看见的,与我们一般所使用的 Python 的程序相比其实并没有什么特殊的区别。我们接下来将看到如何在其它 Python 程序中使用这一模块。 +上方所呈现的就是一个简单的_模块_。正如你所看见的,与我们一般所使用的 Python 的程序相比其实并没有什么特殊的区别。我们接下来将看到如何在其它 Python 程序中使用这一模块。 要记住该模块应该放置于与其它我们即将导入这一模块的程序相同的目录下,或者是放置在 `sys.path` 所列出的其中一个目录下。 另一个模块(保存为 `mymodule_demo.py`): -
{% include "./programs/mymodule_demo.py" %}
+```python +import mymodule + +mymodule.say_hi() +print('Version', mymodule.__version__) + +``` 输出: -
{% include "./programs/mymodule_demo.txt" %}
+```python +$ python mymodule_demo.py +Hi, this is mymodule speaking. +Version 0.1 + +``` **它是如何工作的** @@ -99,7 +149,12 @@ print("Square root of 16 is", sqrt(16)) 下面是一个使用 `from...import` 语法的范本(保存为 `mymodule_demo2.py`): -
{% include "./programs/mymodule_demo2.py" %}
+```python +from mymodule import say_hi, __version__ + +say_hi() +print('Version', __version__) +``` `mymodule_demo2.py` 所输出的内容与 `mymodule_demo.py` 所输出的内容是一样的。 @@ -113,22 +168,17 @@ from mymodule import * 这将导入诸如 `say_hi` 等所有公共名称,但不会导入 `__version__` 名称,因为后者以双下划线开头。 -> **警告:**要记住你应该避免使用 import * 这种形式,即 `from mymodule import * `。 - - +> \*\*警告:\*\*要记住你应该避免使用 import \* 这种形式,即 `from mymodule import *` 。 > **Python 之禅** -> -> Python 的一大指导原则是“明了胜过晦涩”[^2]。你可以通过在 Python 中运行 `import this` 来了解更多内容。 +> +> Python 的一大指导原则是“明了胜过晦涩”。你可以通过在 Python 中运行 `import this` 来了解更多内容。 -## `dir` 函数 {#dir-function} +## `dir` 函数 -内置的 `dir()` 函数能够返回由对象所定义的名称列表。 -如果这一对象是一个模块,则该列表会包括函数内所定义的函数、类与变量。 +内置的 `dir()` 函数能够返回由对象所定义的名称列表。 如果这一对象是一个模块,则该列表会包括函数内所定义的函数、类与变量。 -该函数接受参数。 -如果参数是模块名称,函数将返回这一指定模块的名称列表。 -如果没有提供参数,函数将返回当前模块的名称列表。 +该函数接受参数。 如果参数是模块名称,函数将返回这一指定模块的名称列表。 如果没有提供参数,函数将返回当前模块的名称列表。 案例: @@ -169,9 +219,9 @@ $ python 给了观察 `dir` 函数的操作,我们定义了一个新的变量 `a` 并为其赋予了一个值,然后在检查 `dir` 返回的结果,我们就能发现,同名列表中出现了一个新的值。我们通过 `del` 语句移除了一个变量或是属性,这一变化再次反映在 `dir` 函数所处的内容中。 -关于 `del` 的一个小小提示——这一语句用于*删除*一个变量或名称,当这一语句运行后,在本例中即 `del a`,你便不再能访问变量 `a`——它将如同从未存在过一般。 +关于 `del` 的一个小小提示——这一语句用于_删除_一个变量或名称,当这一语句运行后,在本例中即 `del a`,你便不再能访问变量 `a`——它将如同从未存在过一般。 -要注意到 `dir()` 函数能对*任何*对象工作。例如运行 `dir(str)` 可以访问 `str`(String,字符串)类的属性。 +要注意到 `dir()` 函数能对_任何_对象工作。例如运行 `dir(str)` 可以访问 `str`(String,字符串)类的属性。 同时,还有一个 [`vars()`](http://docs.python.org/3/library/functions.html#vars) 函数也可以返回给你这些值的属性,但只是可能,它并不能针对所有类都能正常工作。 @@ -201,7 +251,7 @@ $ python - bar.py ``` -包是一种能够方便地分层组织模块的方式。你将在 [标准库](./17.stdlib.md#stdlib) 中看到许多有关于此的实例。 +包是一种能够方便地分层组织模块的方式。你将在 [标准库](17.stdlib.md#stdlib) 中看到许多有关于此的实例。 ## 总结 @@ -211,8 +261,6 @@ $ python 接下来,我们将学习一些有趣的概念,它们被称作数据结构。 -**** - -[^1]: 原文作 Byte-compiled .pyc Files,沈洁元译本译作“字节编译的 .pyc 文件”。 +*** -[^2]: 原文作 Explicit is better than Implicit,如果使用前面章节出现过的术语概念,也可理解为“显式胜过隐式”。 \ No newline at end of file +1. 原文作 Explicit is better than Implicit,如果使用前面章节出现过的术语概念,也可理解为“显式胜过隐式”。 diff --git a/12.data_structures.md b/12.data_structures.md index 1fb067c7..ec6a035b 100644 --- a/12.data_structures.md +++ b/12.data_structures.md @@ -1,44 +1,80 @@ -# 数据结构 {#data-structures} +# 数据结构 -数据结构(Data Structures)基本上人如其名——它们只是一种*结构*,能够将一些*数据*聚合在一起。换句话说,它们是用来存储一系列相关数据的集合。 +数据结构(Data Structures)基本上人如其名——它们只是一种_结构_,能够将一些_数据_聚合在一起。换句话说,它们是用来存储一系列相关数据的集合。 Python 中有四种内置的数据结构——_列表(List)、元组(Tuple)、字典(Dictionary)和集合(Set)_。我们将了解如何使用它们,并利用它们将我们的编程之路变得更加简单。 ## 列表 -`列表` 是一种用于保存一系列有序项目的集合,也就是说,你可以利用列表保存一串项目的*序列*。想象起来也不难,你可以想象你有一张购物清单,上面列出了需要购买的商品,除开在购物清单上你可能为每件物品都单独列一行,在 Python 中你需要在它们之间多加上一个逗号。 +`列表` 是一种用于保存一系列有序项目的集合,也就是说,你可以利用列表保存一串项目的_序列_。想象起来也不难,你可以想象你有一张购物清单,上面列出了需要购买的商品,除开在购物清单上你可能为每件物品都单独列一行,在 Python 中你需要在它们之间多加上一个逗号。 -项目的列表应该用方括号括起来,这样 Python 才能理解到你正在指定一张列表。一旦你创建了一张列表,你可以添加、移除或搜索列表中的项目。既然我们可以添加或删除项目,我们会说列表是一种*可变的(Mutable)*数据类型,意即,这种类型是可以被改变的。 +项目的列表应该用方括号括起来,这样 Python 才能理解到你正在指定一张列表。一旦你创建了一张列表,你可以添加、移除或搜索列表中的项目。既然我们可以添加或删除项目,我们会说列表是一种\*可变的(Mutable)\*数据类型,意即,这种类型是可以被改变的。 ## 有关对象与类的快速介绍 -虽然到目前为止我经常推迟有关对象(Object)与类(Class)的讨论,但现在对它们进行稍许解释能够有助于你更好地理解列表。我们将在[后面的章节](./14.oop.md#oop)讨论有关它们的更多细节。 +虽然到目前为止我经常推迟有关对象(Object)与类(Class)的讨论,但现在对它们进行稍许解释能够有助于你更好地理解列表。我们将在[后面的章节](14.oop.md#oop)讨论有关它们的更多细节。 -列表是使用对象与类的实例。当我们启用一个变量 `i` 并将整数 `5` 赋值给它时,你可以认为这是在创建一个 `int` *类*(即类型)之下的*对象*(即实例) `i`。实际上,你可以阅读 `help(int)` 来了解更多内容。 +列表是使用对象与类的实例。当我们启用一个变量 `i` 并将整数 `5` 赋值给它时,你可以认为这是在创建一个 `int` _类_(即类型)之下的_对象_(即实例) `i`。实际上,你可以阅读 `help(int)` 来了解更多内容。 -一个类也可以带有*方法(Method)*,也就是说对这个类定义仅对于它启用某个函数。只有当你拥有一个属于该类的对象时,你才能使用这些功能。举个例子,Python 为 `list` 类提供了一种 `append` 方法,能够允许你向列表末尾添加一个项目。例如 `mylist.append('an item')` 将会向列表 `mylist` 添加一串字符串。在这里要注意到我们通过使用点号的方法来访问对象。 +一个类也可以带有_方法(Method)_,也就是说对这个类定义仅对于它启用某个函数。只有当你拥有一个属于该类的对象时,你才能使用这些功能。举个例子,Python 为 `list` 类提供了一种 `append` 方法,能够允许你向列表末尾添加一个项目。例如 `mylist.append('an item')` 将会向列表 `mylist` 添加一串字符串。在这里要注意到我们通过使用点号的方法来访问对象。 -一个类同样也可以具有*字段(Field)*,它是只为该类定义且只为该类所用的变量。只有当你拥有一个属于该类的对象时,你才能够使用这些变量或名称。字段同样可以通过点号来访问,例如 `mylist.field`。 +一个类同样也可以具有_字段(Field)_,它是只为该类定义且只为该类所用的变量。只有当你拥有一个属于该类的对象时,你才能够使用这些变量或名称。字段同样可以通过点号来访问,例如 `mylist.field`。 案例(保存为 `ds_using_list.py`): -
{% include "./programs/ds_using_list.py" %}
+```python +# This is my shopping list +shoplist = ['apple', 'mango', 'carrot', 'banana'] + +print('I have', len(shoplist), 'items to purchase.') + +print('These items are:', end=' ') +for item in shoplist: + print(item, end=' ') + +print('\nI also have to buy rice.') +shoplist.append('rice') +print('My shopping list is now', shoplist) + +print('I will sort my list now') +shoplist.sort() +print('Sorted shopping list is', shoplist) + +print('The first item I will buy is', shoplist[0]) +olditem = shoplist[0] +del shoplist[0] +print('I bought the', olditem) +print('My shopping list is now', shoplist) + +``` 输出: -
{% include "./programs/ds_using_list.txt" %}
+```python +$ python ds_using_list.py +I have 4 items to purchase. +These items are: apple mango carrot banana +I also have to buy rice. +My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice'] +I will sort my list now +Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice'] +The first item I will buy is apple +I bought the apple +My shopping list is now ['banana', 'carrot', 'mango', 'rice'] + +``` **它是如何工作的** -变量 `shoplist` 是一张为即将前往市场的某人准备的购物清单。在 `shoplist` 中,我们只存储了一些字符串,它们是我们需要购买的物品的名称,但是你可以向列表中添加_任何类型的对象_,包括数字,甚至是其它列表。 +变量 `shoplist` 是一张为即将前往市场的某人准备的购物清单。在 `shoplist` 中,我们只存储了一些字符串,它们是我们需要购买的物品的名称,但是你可以向列表中添加\_任何类型的对象\_,包括数字,甚至是其它列表。 -我们还使用 `for...in` 循环来遍历列表中的每一个项目。学习到现在,你必须有一种列表也是一个序列的意识。有关序列的特性将会在[稍后的章节](#sequence)予以讨论。 +我们还使用 `for...in` 循环来遍历列表中的每一个项目。学习到现在,你必须有一种列表也是一个序列的意识。有关序列的特性将会在[稍后的章节](12.data\_structures.md#sequence)予以讨论。 在这里要注意在调用 `print` 函数时我们使用 `end` 参数,这样就能通过一个空格来结束输出工作,而不是通常的换行。 接下来,如我们讨论过的那般,我们通过列表对象中的 `append` 方法向列表中添加一个对象。然后,我们将列表简单地传递给 `print` 函数,整洁且完整地打印出列表内容,以此来检查项目是否被切实地添加进列表之中。 -接着,我们列表的 `sort` 方法对列表进行排序。在这里要着重理解到这一方法影响到的是列表本身,而不会返回一个修改过的列表——这与修改字符串的方式并不相同。同时,这也是我们所说的,列表是_可变的(Mutable)_而字符串是_不可变的(Immutable)_。 +接着,我们列表的 `sort` 方法对列表进行排序。在这里要着重理解到这一方法影响到的是列表本身,而不会返回一个修改过的列表——这与修改字符串的方式并不相同。同时,这也是我们所说的,列表是\_可变的(Mutable)_而字符串是\_不可变的(Immutable)_。 随后,当我们当我们在市场上买回某件商品时,我们需要从列表中移除它。我们通过使用 `del` 语句来实现这一需求。在这里,我们将给出我们希望从列表中移除的商品,`del` 语句则会为我们从列表中移除对应的项目。我们希望移除列表中的第一个商品,因此我们使用 `del shoplist[0]`(要记住 Python 从 0 开始计数)。 @@ -46,7 +82,7 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple ## 元组 -元组(Tuple)用于将多个对象保存到一起。你可以将它们近似地看作列表,但是元组不能提供列表类能够提供给你的广泛的功能。元组的一大特征类似于字符串,它们是*不可变的*,也就是说,你不能编辑或更改元组。 +元组(Tuple)用于将多个对象保存到一起。你可以将它们近似地看作列表,但是元组不能提供列表类能够提供给你的广泛的功能。元组的一大特征类似于字符串,它们是_不可变的_,也就是说,你不能编辑或更改元组。 元组是通过特别指定项目来定义的,在指定项目时,你可以给它们加上括号,并在括号内部用逗号进行分隔。 @@ -54,33 +90,56 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple 案例(保存为 `ds_using_tuple.py`): -
{% include "./programs/ds_using_tuple.py" %}
+```python +# 我会推荐你总是使用括号 +# 来指明元组的开始与结束 +# 尽管括号是一个可选选项。 +# 明了胜过晦涩,显式优于隐式。 +zoo = ('python', 'elephant', 'penguin') +print('Number of animals in the zoo is', len(zoo)) + +new_zoo = 'monkey', 'camel', zoo +print('Number of cages in the new zoo is', len(new_zoo)) +print('All animals in new zoo are', new_zoo) +print('Animals brought from old zoo are', new_zoo[2]) +print('Last animal brought from old zoo is', new_zoo[2][2]) +print('Number of animals in the new zoo is', + len(new_zoo)-1+len(new_zoo[2])) + +``` 输出: -
{% include "./programs/ds_using_tuple.txt" %}
+```python +$ python ds_using_tuple.py +Number of animals in the zoo is 3 +Number of cages in the new zoo is 3 +All animals in new zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin')) +Animals brought from old zoo are ('python', 'elephant', 'penguin') +Last animal brought from old zoo is penguin +Number of animals in the new zoo is 5 + +``` **它是如何工作的** -变量 `zoo` 指的是一个包含项目的元组。我们能够看到 `len` 函数在此处用来获取元组的长度。这也表明元组同时也是一个[序列](#sequence)。 +变量 `zoo` 指的是一个包含项目的元组。我们能够看到 `len` 函数在此处用来获取元组的长度。这也表明元组同时也是一个[序列](12.data\_structures.md#sequence)。 现在,我们将这些动物从即将关闭的老动物园(Zoo)转移到新的动物园中。因此,`new_zoo` 这一元组包含了一些本已存在的动物以及从老动物园转移过去的动物。让我们回到话题中来,在这里要注意到元组中所包含的元组不会失去其所拥有的身份。 -如同我们在列表里所做的那般,我们可以通过在方括号中指定项目所处的位置来访问元组中的各个项目。这种使用方括号的形式被称作_索引(Indexing)_运算符。我们通过指定 `new_zoo[2]` 来指定 `new_zoo` 中的第三个项目,我们也可以通过指定 `new_zoo[2][2]` 来指定 `new_zoo` 元组中的第三个项目中的第三个项目[^1]。一旦你习惯了这种语法你就会觉得这其实非常简单。 +如同我们在列表里所做的那般,我们可以通过在方括号中指定项目所处的位置来访问元组中的各个项目。这种使用方括号的形式被称作\_索引(Indexing)\_运算符。我们通过指定 `new_zoo[2]` 来指定 `new_zoo` 中的第三个项目,我们也可以通过指定 `new_zoo[2][2]` 来指定 `new_zoo` 元组中的第三个项目中的第三个项目。一旦你习惯了这种语法你就会觉得这其实非常简单。 > **包含 0 或 1 个项目的元组** -> +> > 一个空的元组由一对圆括号构成,就像 `myempty = ()` 这样。然而,一个只拥有一个项目的元组并不像这样简单。你必须在第一个(也是唯一一个)项目的后面加上一个逗号来指定它,如此一来 Python 才可以识别出在这个表达式想表达的究竟是一个元组还是只是一个被括号所环绕的对象,也就是说,如果你想指定一个包含项目 `2` 的元组,你必须指定 `singleton = (2, )`。 - - > **针对 Perl 程序员的提示** -> +> > 列表中的列表不会丢失其标识,即列表不会像在 Perl 里那般会被打散(Flattened)。这同样也适用于元组中的元组、列表中的元组或元组中的列表等等情况。对于 Python 而言,它们只是用一个对象来存储另一个对象,不过仅此而已。 ## 字典 -字典就像一本地址簿,如果你知道了他或她的姓名,你就可以在这里找到其地址或是能够联系上对方的更多详细信息,换言之,我们将*键值(Keys)*(即姓名)与*值(Values)*(即地址等详细信息)联立到一起。在这里要注意到键值必须是唯一的,正如在现实中面对两个完全同名的人你没办法找出有关他们的正确信息。 +字典就像一本地址簿,如果你知道了他或她的姓名,你就可以在这里找到其地址或是能够联系上对方的更多详细信息,换言之,我们将_键值(Keys)_(即姓名)与_值(Values)_(即地址等详细信息)联立到一起。在这里要注意到键值必须是唯一的,正如在现实中面对两个完全同名的人你没办法找出有关他们的正确信息。 另外要注意的是你只能使用不可变的对象(如字符串)作为字典的键值,但是你可以使用可变或不可变的对象作为字典中的值。基本上这段话也可以翻译为你只能使用简单对象作为键值。 @@ -92,11 +151,49 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple 案例(保存为 `ds_using_dict.py`): -
{% include "./programs/ds_using_dict.py" %}
+```python +# “ab”是地址(Address)簿(Book)的缩写 + +ab = { + 'Swaroop': 'swaroop@swaroopch.com', + 'Larry': 'larry@wall.org', + 'Matsumoto': 'matz@ruby-lang.org', + 'Spammer': 'spammer@hotmail.com' +} + +print("Swaroop's address is", ab['Swaroop']) + +# 删除一对键值—值配对 +del ab['Spammer'] + +print('\nThere are {} contacts in the address-book\n'.format(len(ab))) + +for name, address in ab.items(): + print('Contact {} at {}'.format(name, address)) + +# 添加一对键值—值配对 +ab['Guido'] = 'guido@python.org' + +if 'Guido' in ab: + print("\nGuido's address is", ab['Guido']) + +``` 输出: -
{% include "./programs/ds_using_dict.txt" %}
+```python +$ python ds_using_dict.py +Swaroop's address is swaroop@swaroopch.com + +There are 3 contacts in the address-book + +Contact Swaroop at swaroop@swaroopch.com +Contact Matsumoto at matz@ruby-lang.org +Contact Larry at larry@wall.org + +Guido's address is guido@python.org + +``` **它是如何工作的** @@ -113,28 +210,72 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple 要想了解有关 `dict` 类的更多方法,请参阅 `help(dict)`。 > **关键字参数与字典** -> -> 如果你曾在你的函数中使用过关键词参数,那么你就已经使用过字典了!你只要这么想——你在定义函数时的参数列表时,就指定了相关的键值—值配对。当你在你的函数中访问某一变量时,它其实就是在访问字典中的某个键值。(在编译器设计的术语中,这叫作_符号表(Symbol Table)_) +> +> 如果你曾在你的函数中使用过关键词参数,那么你就已经使用过字典了!你只要这么想——你在定义函数时的参数列表时,就指定了相关的键值—值配对。当你在你的函数中访问某一变量时,它其实就是在访问字典中的某个键值。(在编译器设计的术语中,这叫作\_符号表(Symbol Table)\_) -## 序列 {#sequence} +## 序列 列表、元组和字符串可以看作序列(Sequence)的某种表现形式,可是究竟什么是序列,它又有什么特别之处? -序列的主要功能是*资格测试(Membership Test)*(也就是 `in` 与 `not in` 表达式)和*索引操作(Indexing Operations)*,它们能够允许我们直接获取序列中的特定项目。 +序列的主要功能是_资格测试(Membership Test)_(也就是 `in` 与 `not in` 表达式)和_索引操作(Indexing Operations)_,它们能够允许我们直接获取序列中的特定项目。 -上面所提到的序列的三种形态——列表、元组与字符串,同样拥有一种*切片(Slicing)*运算符,它能够允许我们序列中的某段切片——也就是序列之中的一部分。 +上面所提到的序列的三种形态——列表、元组与字符串,同样拥有一种\*切片(Slicing)\*运算符,它能够允许我们序列中的某段切片——也就是序列之中的一部分。 案例(保存为 `ds_seq.py`): -
{% include "./programs/ds_seq.py" %}
+```python +shoplist = ['apple', 'mango', 'carrot', 'banana'] +name = 'swaroop' + +# Indexing or 'Subscription' operation # +# 索引或“下标(Subscription)”操作符 # +print('Item 0 is', shoplist[0]) +print('Item 1 is', shoplist[1]) +print('Item 2 is', shoplist[2]) +print('Item 3 is', shoplist[3]) +print('Item -1 is', shoplist[-1]) +print('Item -2 is', shoplist[-2]) +print('Character 0 is', name[0]) + +# Slicing on a list # +print('Item 1 to 3 is', shoplist[1:3]) +print('Item 2 to end is', shoplist[2:]) +print('Item 1 to -1 is', shoplist[1:-1]) +print('Item start to end is', shoplist[:]) + +# 从某一字符串中切片 # +print('characters 1 to 3 is', name[1:3]) +print('characters 2 to end is', name[2:]) +print('characters 1 to -1 is', name[1:-1]) +print('characters start to end is', name[:]) + +``` 输出: -
{% include "./programs/ds_seq.txt" %}
+``` +$ python ds_seq.py +Item 0 is apple +Item 1 is mango +Item 2 is carrot +Item 3 is banana +Item -1 is banana +Item -2 is carrot +Character 0 is s +Item 1 to 3 is ['mango', 'carrot'] +Item 2 to end is ['carrot', 'banana'] +Item 1 to -1 is ['mango', 'carrot'] +Item start to end is ['apple', 'mango', 'carrot', 'banana'] +characters 1 to 3 is wa +characters 2 to end is aroop +characters 1 to -1 is waroo +characters start to end is swaroop + +``` **它是如何工作的** -首先,我们已经了解了如何通过使用索引来获取序列中的各个项目。这也被称作_下标操作(Subscription Operation)_。如上所示,每当你在方括号中为序列指定一个数字,Python 将获取序列中与该位置编号相对应的项目。要记得 Python 从 0 开始计数。因此 `shoplist[0]` 将获得 `shoplist` 序列中的第一个项目,而 `shoplist[3]` 将获得第四个项目。 +首先,我们已经了解了如何通过使用索引来获取序列中的各个项目。这也被称作\_下标操作(Subscription Operation)\_。如上所示,每当你在方括号中为序列指定一个数字,Python 将获取序列中与该位置编号相对应的项目。要记得 Python 从 0 开始计数。因此 `shoplist[0]` 将获得 `shoplist` 序列中的第一个项目,而 `shoplist[3]` 将获得第四个项目。 索引操作也可以使用负数,在这种情况下,位置计数将从队列的末尾开始。因此,`shoplist[-1]` 指的是序列的最后一个项目,`shoplist[-2]` 将获取序列中倒数第二个项目。 @@ -142,11 +283,11 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple 在切片操作中,第一个数字(冒号前面的那位)指的是切片开始的位置,第二个数字(冒号后面的那位)指的是切片结束的位置。如果第一位数字没有指定,Python 将会从序列的起始处开始操作。如果第二个数字留空,Python 将会在序列的末尾结束操作。要注意的是切片操作会在开始处返回 _start_,并在 _end_ 前面的位置结束工作。也就是说,序列切片将包括起始位置,但不包括结束位置。 -因此,`shoplist[1:3]` 返回的序列的一组切片将从位置 1 开始,包含位置 2 并在位置 3 时结束,因此,这块*切片*返回的是两个项目。类似地,`shoplist[:]` 返回的是整个序列。 +因此,`shoplist[1:3]` 返回的序列的一组切片将从位置 1 开始,包含位置 2 并在位置 3 时结束,因此,这块_切片_返回的是两个项目。类似地,`shoplist[:]` 返回的是整个序列。 你同样可以在切片操作中使用负数位置。使用负数时位置将从序列末端开始计算。例如,`shoplist[:-1]` 强返回一组序列切片,其中不包括序列的最后一项项目,但其它所有项目都包含其中。 -你同样可以在切片操作中提供第三个参数,这一参数将被视为切片的_步长(Step)_(在默认情况下,步长大小为 1): +你同样可以在切片操作中提供第三个参数,这一参数将被视为切片的\_步长(Step)\_(在默认情况下,步长大小为 1): ```python >>> shoplist = ['apple', 'mango', 'carrot', 'banana'] @@ -166,7 +307,7 @@ Python 中有四种内置的数据结构——_列表(List)、元组(Tuple ## 集合 -集合(Set)是简单对象的_无序_集合(Collection)。当集合中的项目存在与否比起次序或其出现次数更加重要时,我们就会使用集合。 +集合(Set)是简单对象的\_无序\_集合(Collection)。当集合中的项目存在与否比起次序或其出现次数更加重要时,我们就会使用集合。 通过使用集合,你可以测试某些对象的资格或情况,检查它们是否是其它集合的子集,找到两个集合的交集,等等。 @@ -189,31 +330,65 @@ True 这个案例几乎不言自明,因为它涉及的是学校所教授的数学里的基础集合知识。 -## 引用[^2] +## 引用 -当你创建了一个对象并将其分配给某个变量时,变量只会_查阅(Refer)_某个对象,并且它也不会代表对象本身。也就是说,变量名只是指向你计算机内存中存储了相应对象的那一部分。这叫作将名称*绑定(Binding)*给那一个对象。 +当你创建了一个对象并将其分配给某个变量时,变量只会\_查阅(Refer)\_某个对象,并且它也不会代表对象本身。也就是说,变量名只是指向你计算机内存中存储了相应对象的那一部分。这叫作将名称\*绑定(Binding)\*给那一个对象。 一般来说,你不需要去关心这个,不过由于这一引用操作困难会产生某些微妙的效果,这是需要你注意的: 案例(保存为 `ds_reference.py`): -
{% include "./programs/ds_reference.py" %}
+```python +print('Simple Assignment') +shoplist = ['apple', 'mango', 'carrot', 'banana'] +# mylist 只是指向同一对象的另一种名称 +mylist = shoplist + +# 我购买了第一项项目,所以我将其从列表中删除 +del shoplist[0] + +print('shoplist is', shoplist) +print('mylist is', mylist) +# 注意到 shoplist 和 mylist 二者都 +# 打印出了其中都没有 apple 的同样的列表,以此我们确认 +# 它们指向的是同一个对象 + +print('Copy by making a full slice') +# 通过生成一份完整的切片制作一份列表的副本 +mylist = shoplist[:] +# 删除第一个项目 +del mylist[0] + +print('shoplist is', shoplist) +print('mylist is', mylist) +# 注意到现在两份列表已出现不同 + +``` 输出: -
{% include "./programs/ds_reference.txt" %}
+```python +$ python ds_reference.py +Simple Assignment +shoplist is ['mango', 'carrot', 'banana'] +mylist is ['mango', 'carrot', 'banana'] +Copy by making a full slice +shoplist is ['mango', 'carrot', 'banana'] +mylist is ['carrot', 'banana'] + +``` **它是如何工作的** 大部分解释已经在注释中提供。 -你要记住如果你希望创建一份诸如序列等复杂对象的副本(而非整数这种简单的_对象(Object)_),你必须使用切片操作来制作副本。如果你仅仅是将一个变量名赋予给另一个名称,那么它们都将“查阅”同一个对象,如果你对此不够小心,那么它将造成麻烦。 +你要记住如果你希望创建一份诸如序列等复杂对象的副本(而非整数这种简单的\_对象(Object)\_),你必须使用切片操作来制作副本。如果你仅仅是将一个变量名赋予给另一个名称,那么它们都将“查阅”同一个对象,如果你对此不够小心,那么它将造成麻烦。 > **针对 Perl 程序员的提示** -> +> > 要记住列表的赋值语句**不会**创建一份副本。你必须使用切片操作来生成一份序列的副本。 -## 有关字符串的更多内容 {#more-strings} +## 有关字符串的更多内容 在早些时候我们已经详细讨论过了字符串。还有什么可以知道的吗?还真有,想必你还不知道字符串同样也是一种对象,并且它也具有自己的方法,可以做到检查字符串中的一部分或是去掉空格等几乎一切事情! @@ -221,11 +396,35 @@ True 案例(保存为 `ds_str_methods.py`): -
{% include "./programs/ds_str_methods.py" %}
+```python +# 这是一个字符串对象 +name = 'Swaroop' + +if name.startswith('Swa'): + print('Yes, the string starts with "Swa"') + +if 'a' in name: + print('Yes, it contains the string "a"') + +if name.find('war') != -1: + print('Yes, it contains the string "war"') + +delimiter = '_*_' +mylist = ['Brazil', 'Russia', 'India', 'China'] +print(delimiter.join(mylist)) + +``` 输出: -
{% include "./programs/ds_str_methods.txt" %}
+```python +$ python ds_str_methods.py +Yes, the string starts with "Swa" +Yes, it contains the string "a" +Yes, it contains the string "war" +Brazil_*_Russia_*_India_*_China + +``` **它是如何工作的** @@ -239,9 +438,6 @@ True 现在我们已经具备了诸多有关 Python 的基本知识,接下来我们将会了解如何设计并编写一款真实的 Python 程序。 +*** - -**** -[^1]: 第一个“第三个项目”可以是指元组中的元组。 - -[^2]: 原文作“Reference”,沈洁元译本译作“参考”。此处译名尚存疑,如有更好的翻译建议还请指出。 \ No newline at end of file +1. 原文作“Reference”,沈洁元译本译作“参考”。此处译名尚存疑,如有更好的翻译建议还请指出。 diff --git a/13.problem_solving.md b/13.problem_solving.md index e0e2b32f..9d7c248d 100644 --- a/13.problem_solving.md +++ b/13.problem_solving.md @@ -1,6 +1,6 @@ # 解决问题 -我们已经探索了 Python 语言中的许多部分,现在我们将通过设计并编写一款程序来了解如何把这些部分组合到一起。这些程序一定是能_做到_一些有用的事情。这其中的方法就是去学习如何靠你自己来编写一份 Python 脚本。 +我们已经探索了 Python 语言中的许多部分,现在我们将通过设计并编写一款程序来了解如何把这些部分组合到一起。这些程序一定是能\_做到\_一些有用的事情。这其中的方法就是去学习如何靠你自己来编写一份 Python 脚本。 ## 问题 @@ -8,39 +8,93 @@ > 我想要一款程序来备份我所有的重要文件。 -虽然这是一个简单的问题,但是其中并没有足够的信息有助于让我们开始规划一份解决方案。我们需要进行一些*分析(Analysis)*。例如,我们应该如何指定_哪些_文件是我们需要备份的?它们应该_如何_进行备份?储存到_哪里_? +虽然这是一个简单的问题,但是其中并没有足够的信息有助于让我们开始规划一份解决方案。我们需要进行一些_分析(Analysis)_。例如,我们应该如何指定\_哪些\_文件是我们需要备份的?它们应该\_如何\_进行备份?储存到\_哪里\_? -在正确地分析了这些问题过后,我们便开始*设计(Design)*我们的程序。我们将列出一份关于我们的程序应如何运转的清单。在这个案例中,我已经编写了如下清单来说明_我_将如何工作。如果由你来设计程序,你可能不会做出同样的分析,因为每个人都有其自己的行事方式,所以出现不同是完全正常、且正确的。 +在正确地分析了这些问题过后,我们便开始\*设计(Design)\*我们的程序。我们将列出一份关于我们的程序应如何运转的清单。在这个案例中,我已经编写了如下清单来说明\_我\_将如何工作。如果由你来设计程序,你可能不会做出同样的分析,因为每个人都有其自己的行事方式,所以出现不同是完全正常、且正确的。 -- 需要备份的文件与目录应在一份列表中予以指定。 -- 备份必须存储在一个主备份目录中。 -- 备份文件将打包压缩成 zip 文件。 -- zip 压缩文件的文件名由当前日期与时间构成。 -- 我们使用在任何 GNU/Linux 或 Unix 发行版中都会默认提供的标准 `zip` 命令进行打包。在这里你需要了解到只要有命令行界面,你就可以使用任何需要用到的压缩或归档命令。 +* 需要备份的文件与目录应在一份列表中予以指定。 +* 备份必须存储在一个主备份目录中。 +* 备份文件将打包压缩成 zip 文件。 +* zip 压缩文件的文件名由当前日期与时间构成。 +* 我们使用在任何 GNU/Linux 或 Unix 发行版中都会默认提供的标准 `zip` 命令进行打包。在这里你需要了解到只要有命令行界面,你就可以使用任何需要用到的压缩或归档命令。 > **针对 Windows 用户的提示** -> -> Windows 用户可以从 [GnuWin32 项目页面](http://gnuwin32.sourceforge.net/packages/zip.htm) 上下载并[安装](http://gnuwin32.sourceforge.net/downlinks/zip.php) `zip` 命令,并将 `C:\Program Files\GnuWin32\bin` 添加至你的系统的 `PATH` 环境变量中,这一操作过程与[我们为使系统识别 Python 命令本身所做的事情](./installation.md#dos-prompt)相同。 +> +> Windows 用户可以从 [GnuWin32 项目页面](http://gnuwin32.sourceforge.net/packages/zip.htm) 上下载并[安装](http://gnuwin32.sourceforge.net/downlinks/zip.php) `zip` 命令,并将 `C:\Program Files\GnuWin32\bin` 添加至你的系统的 `PATH` 环境变量中,这一操作过程与[我们为使系统识别 Python 命令本身所做的事情](installation.md#dos-prompt)相同。 ## 解决方案 -由于我们的程序设计方案现在已经相当稳定,我们便可以开始编写代码,这个过程我们称之为*实现(Implementation)*我们的解决方案。 +由于我们的程序设计方案现在已经相当稳定,我们便可以开始编写代码,这个过程我们称之为\*实现(Implementation)\*我们的解决方案。 将下述代码保存为 `backup_ver1.py`: -
{% include "./programs/backup_ver1.py" %}
+```python +import os +import time + +# 1. 需要备份的文件与目录将被 +# 指定在一个列表中。 +# 例如在 Windows 下: +# source = ['"C:\\My Documents"', 'C:\\Code'] +# 又例如在 Mac OS X 与 Linux 下: +source = ['/Users/swa/notes'] +# 在这里要注意到我们必须在字符串中使用双引号 +# 用以括起其中包含空格的名称。 + +#2. 备份文件必须存储在一个 +#主备份目录中 +#例如在 Windows 下: +# target_dir = 'E:\\Backup' +# 又例如在 Mac OS X 和 Linux 下: +target_dir = '/Users/swa/backup' +# 要记得将这里的目录地址修改至你将使用的路径 + +# 3. 备份文件将打包压缩成 zip 文件。 +# 4. zip 压缩文件的文件名由当前日期与时间构成。 +target = target_dir + os.sep + \ + time.strftime('%Y%m%d%H%M%S') + '.zip' + +# 如果目标目录还不存在,则进行创建 +if not os.path.exists(target_dir): + os.mkdir(target_dir) # 创建目录 + +# 5. 我们使用 zip 命令将文件打包成 zip 格式 +zip_command = 'zip -r {0} {1}'.format(target, + ' '.join(source)) + +# 运行备份 +print('Zip command is:') +print(zip_command) +print('Running:') +if os.system(zip_command) == 0: + print('Successful backup to', target) +else: + print('Backup FAILED') + +``` 输出: -
{% include "./programs/backup_ver1.txt" %}
+``` +$ python backup_ver1.py +Zip command is: +zip -r /Users/swa/backup/20140328084844.zip /Users/swa/notes +Running: + adding: Users/swa/notes/ (stored 0%) + adding: Users/swa/notes/blah1.txt (stored 0%) + adding: Users/swa/notes/blah2.txt (stored 0%) + adding: Users/swa/notes/blah3.txt (stored 0%) +Successful backup to /Users/swa/backup/20140328084844.zip -现在,我们正处于*测试(Testing)*阶段,在这一阶段我们测试我们的程序是否能正常工作。如果其行为不符合我们的预期,那么我们需要对我们的程序进行 *Debug* 工作,也就是说,移除程序中的 *Bug*(错误)。 +``` + +现在,我们正处于_测试(Testing)_阶段,在这一阶段我们测试我们的程序是否能正常工作。如果其行为不符合我们的预期,那么我们需要对我们的程序进行 _Debug_ 工作,也就是说,移除程序中的 _Bug_(错误)。 如果上面的程序不能够正常工作,复制打印在 `Zip command is` 后面的命令,将其粘贴至 shell(在 GNU/Linux 与 Mac OS X 环境中)或 `cmd`(对于 Windows 环境),看看存在什么错误并尝试将其修复。同时你还需要检查 zip 命令手册来看看是不是哪里存在错误。如果这条命令成功运行,那么可能是错误可能存在在 Python 程序本身之中,因此你需要检查你的程序是否如上面所展示那番。 **它是如何工作的** -你会注意到我们是如何一步步将我们的*设计*转化为*代码*的。 +你会注意到我们是如何一步步将我们的_设计_转化为_代码_的。 我们首先导入 `os` 与 `time` 模块以准备使用它们。然后,我们在 `source` 列表中指定我们需要备份的文件与目录。我们需要存储我们所有备份文件的目标目录在 `target_dir` 变量中予以指定。我们将要创建的 zip 归档文件的名字由当前日期与时间构成,在这里通过 `time.strftime()` 函数来创建。文件名将以 `.zip` 作为扩展名,并存储在 `target_dir` 目录中。 @@ -48,37 +102,100 @@ `time.strftime()` 函数会遵循某些格式(Specification),其中一种就如我们在上方程序中所使用的那样。`%Y` 将被替换成带有具体世纪的年份。`%m` 将会被替换成以 `01` 至 `12` 的十进制数所表示的月份。有关这些格式的全部列表可以在[ Python 参考手册](http://docs.python.org/3/library/time.html#time.strftime)中查询到。 -我们使用_连接(Concatenates)_字符串的加法(`+`)运算符来创建目标 zip 文件的文件名,也就是说,它将两个字符串连接到一起并返回一个新的字符串。然后,我们创建了一串字符串 `zip_command`,其中包括了我们要执行的命令。如果这条命令不能正常工作,你可以把它拷贝到 Shell(GNU/Linux 终端或 DOS 提示符)中进行检查。 +我们使用\_连接(Concatenates)\_字符串的加法(`+`)运算符来创建目标 zip 文件的文件名,也就是说,它将两个字符串连接到一起并返回一个新的字符串。然后,我们创建了一串字符串 `zip_command`,其中包括了我们要执行的命令。如果这条命令不能正常工作,你可以把它拷贝到 Shell(GNU/Linux 终端或 DOS 提示符)中进行检查。 我们使用的 `zip` 命令会有一些选项与参数需要传递。`-r` 选项用以指定 zip 命令应该**递归地**(**R**ecursively)对目录进行工作,也就是说它应该包括所有的子文件夹与其中的文件。这两个选项结合到一起并可以指定一个快捷方式作 `-qr`。选项后面跟着的是将要创建的 zip 文件的名称,再往后是需要备份的文件与目录的列表。我们通过使用已经讨论过并已了解该如何运用的的字符串方法 `join` 来将列表 `source` 转换成字符串。 -随后,我们终于可以*运行*这一使用了 `os.system` 函数的命令,这一函数可以使命令像是从*系统*中运行的。也就是说,从 shell 中运行的——如果运行成功,它将返回 `0`,如果运行失败,将返回一个错误代码。 +随后,我们终于可以_运行_这一使用了 `os.system` 函数的命令,这一函数可以使命令像是从_系统_中运行的。也就是说,从 shell 中运行的——如果运行成功,它将返回 `0`,如果运行失败,将返回一个错误代码。 根据命令运行的结果是成功还是失败,我们将打印出与之相应的信息来告诉你备份的结果究竟如何。 就是这样,我们便创建了一份用以备份我们的重要文件的脚本! > **针对 Windows 用户的提示** -> -> 除了使用双反斜杠转义序列,你还可以使用原始字符串。例如使用 `'C:\\Documents'` 或 `r'C:\Documents'`。然而,*不*要使用 `'C:\Documents'`,因为它将被识别为你使用了一个未知的转义序列 `\D` 来结束路径的输入。 +> +> 除了使用双反斜杠转义序列,你还可以使用原始字符串。例如使用 `'C:\\Documents'` 或 `r'C:\Documents'`。然而,_不_要使用 `'C:\Documents'`,因为它将被识别为你使用了一个未知的转义序列 `\D` 来结束路径的输入。 -现在,我们已经拥有了一份可以正常工作的备份脚本,我们可以在任何我们需要备份文件的时候使用它。这被称作软件的*操作(Operation)*或*部署(Deployment)*阶段。 +现在,我们已经拥有了一份可以正常工作的备份脚本,我们可以在任何我们需要备份文件的时候使用它。这被称作软件的\*操作(Operation)_或_部署(Deployment)\*阶段。 上面所展示的程序能够正常工作,但是(通常)第一个程序都不会按照你所期望的进行工作。可能是因为你没有正确地设计程序,或如果你在输入代码时出现了错误。出现这些情况时,在恰当的时候,你需要回到设计阶段,或者你需要对你的程序进行 Debug 工作。 ## 第二版 -我们的第一版脚本已经能够工作了。然而,我们还可以对它作出一些改进,从而使它能够更好地在每一天都可以正常工作。我们将这一阶段称之为软件的*维护(Maintenance)*阶段。 +我们的第一版脚本已经能够工作了。然而,我们还可以对它作出一些改进,从而使它能够更好地在每一天都可以正常工作。我们将这一阶段称之为软件的\*维护(Maintenance)\*阶段。 -我认为有一种颇为有用的改进是起用一种更好的文件命名机制——使用_时间_作为文件名,存储在以当前_日期_为名字的文件夹中,这一文件夹则照常存储在主备份目录下。这种机制的第一个有点在于你的备份会以分层的形式予以存储,从而使得它们能更易于管理。第二个优点是文件名能够更短。第三个优点在于由于只有当天进行了备份才会创建相应的目录,独立的目录能够帮助你快速地检查每天是否都进行了备份。 +我认为有一种颇为有用的改进是起用一种更好的文件命名机制——使用\_时间\_作为文件名,存储在以当前\_日期\_为名字的文件夹中,这一文件夹则照常存储在主备份目录下。这种机制的第一个有点在于你的备份会以分层的形式予以存储,从而使得它们能更易于管理。第二个优点是文件名能够更短。第三个优点在于由于只有当天进行了备份才会创建相应的目录,独立的目录能够帮助你快速地检查每天是否都进行了备份。 保存为 `backup_ver2.py`: -
{% include "./programs/backup_ver2.py" %}
+```python +import os +import time + +# 1. 需要备份的文件与目录将被 +# 指定在一个列表中。 +# 例如在 Windows 下: +# source = ['"C:\\My Documents"', 'C:\\Code'] +# 又例如在 Mac OS X 与 Linux 下: +source = ['/Users/swa/notes'] +# 在这里要注意到我们必须在字符串中使用双引号 +# 用以括起其中包含空格的名称。 + +# 2. 备份文件必须存储在一个 +# 主备份目录中 +# 例如在 Windows 下: +# target_dir = 'E:\\Backup' +# 又例如在 Mac OS X 和 Linux 下: +target_dir = '/Users/swa/backup' +# 要记得将这里的目录地址修改至你将使用的路径 + +# 如果目标目录不存在则创建目录 +if not os.path.exists(target_dir): + os.mkdir(target_dir) # 创建目录 + +# 3. 备份文件将打包压缩成 zip 文件。 +# 4. 将当前日期作为主备份目录下的子目录名称 +today = target_dir + os.sep + time.strftime('%Y%m%d') +# 将当前时间作为 zip 文件的文件名 +now = time.strftime('%H%M%S') + +# zip 文件名称格式 +target = today + os.sep + now + '.zip' + +# 如果子目录尚不存在则创建一个 +if not os.path.exists(today): + os.mkdir(today) + print('Successfully created directory', today) + +# 5. 我们使用 zip 命令将文件打包成 zip 格式 +zip_command = 'zip -r {0} {1}'.format(target, + ' '.join(source)) + +# 运行备份 +print('Zip command is:') +print(zip_command) +print('Running:') +if os.system(zip_command) == 0: + print('Successful backup to', target) +else: + print('Backup FAILED') + +``` 输出: -
{% include "./programs/backup_ver2.txt" %}
+```python +$ python backup_ver2.py +Successfully created directory /Users/swa/backup/20140329 +Zip command is: +zip -r /Users/swa/backup/20140329/073201.zip /Users/swa/notes +Running: + adding: Users/swa/notes/ (stored 0%) + adding: Users/swa/notes/blah1.txt (stored 0%) + adding: Users/swa/notes/blah2.txt (stored 0%) + adding: Users/swa/notes/blah3.txt (stored 0%) +Successful backup to /Users/swa/backup/20140329/073201.zip +``` **它是如何工作的** @@ -92,27 +209,166 @@ 保存为 `backup_ver3.py`: -
{% include "./programs/backup_ver3.py" %}
+```python +import os +import time + +# 1. 需要备份的文件与目录将被 +# 指定在一个列表中。 +# 例如在 Windows 下: +# source = ['"C:\\My Documents"', 'C:\\Code'] +# 又例如在 Mac OS X 与 Linux 下: +source = ['/Users/swa/notes'] +# 在这里要注意到我们必须在字符串中使用双引号 +# 用以括起其中包含空格的名称。 + +# 2. 备份文件必须存储在一个 +# 主备份目录中 +# 例如在 Windows 下: +# target_dir = 'E:\\Backup' +# 又例如在 Mac OS X 和 Linux 下: +target_dir = '/Users/swa/backup' +# 要记得将这里的目录地址修改至你将使用的路径 + +# 如果目标目录还不存在,则进行创建 +if not os.path.exists(target_dir): + os.mkdir(target_dir) # 创建目录 + +# 3. 备份文件将打包压缩成 zip 文件。 +# 4. 将当前日期作为主备份目录下的 +# 子目录名称 +today = target_dir + os.sep + time.strftime('%Y%m%d') +# 将当前时间作为 zip 文件的文件名 +now = time.strftime('%H%M%S') + +# 添加一条来自用户的注释以创建 +# zip 文件的文件名 +comment = input('Enter a comment --> ') +# 检查是否有评论键入 +if len(comment) == 0: + target = today + os.sep + now + '.zip' +else: + target = today + os.sep + now + '_' + + comment.replace(' ', '_') + '.zip' + +# 如果子目录尚不存在则创建一个 +if not os.path.exists(today): + os.mkdir(today) + print('Successfully created directory', today) + +# 5. 我们使用 zip 命令将文件打包成 zip 格式 +zip_command = "zip -r {0} {1}".format(target, + ' '.join(source)) + +# 运行备份 +print('Zip command is:') +print(zip_command) +print('Running:') +if os.system(zip_command) == 0: + print('Successful backup to', target) +else: + print('Backup FAILED') + +``` 输出: -
{% include "./programs/backup_ver3.txt" %}
+``` +$ python backup_ver3.py + File "backup_ver3.py", line 39 + target = today + os.sep + now + '_' + + ^ +SyntaxError: invalid syntax + +``` **它是如何(不)工作的** -*这个程序它跑不起来!*Python 会说程序之中存在着语法错误,这意味着脚本并未拥有 Python 期望看见的结构。当我们观察 Python 给出的错误时,会看见它同时也告诉我们它检测到错误的额地方。所以我们开始从那个地方开始对我们的程序进行 *Debug* 工作。 +_**这个程序它跑不起来!**_Python 会说程序之中存在着语法错误,这意味着脚本并未拥有 Python 期望看见的结构。当我们观察 Python 给出的错误时,会看见它同时也告诉我们它检测到错误的额地方。所以我们开始从那个地方开始对我们的程序进行 _Debug_ 工作。 -仔细观察,我们会发现有一独立的逻辑行被分成了两行物理行,但我们并未指定这两行物理行应该是一起的。基本上,Python 已经发现了该逻辑行中的加法运算符(`+`)没有任何操作数,因此它不知道接下来应当如何继续。因此,我们在程序中作出修正。当我们发现程序中的错误并对其进行修正时,我们称为*“错误修复(Bug Fixing)”*。 +仔细观察,我们会发现有一独立的逻辑行被分成了两行物理行,但我们并未指定这两行物理行应该是一起的。基本上,Python 已经发现了该逻辑行中的加法运算符(`+`)没有任何操作数,因此它不知道接下来应当如何继续。因此,我们在程序中作出修正。当我们发现程序中的错误并对其进行修正时,我们称为**“错误修复(Bug Fixing)”**。 ## 第四版 保存为 `backup_ver4.py`: -
{% include "./programs/backup_ver4.py" %}
+```python +import os +import time + +# 1. 需要备份的文件与目录将被 +# 指定在一个列表中。 +# 例如在 Windows 下: +# source = ['"C:\\My Documents"', 'C:\\Code'] +# 又例如在 Mac OS X 与 Linux 下: +source = ['/Users/swa/notes'] +# 在这里要注意到我们必须在字符串中使用双引号 +# 用以括起其中包含空格的名称。 + +# 2. 备份文件必须存储在一个 +# 主备份目录中 +# 例如在 Windows 下: +# target_dir = 'E:\\Backup' +# 又例如在 Mac OS X 和 Linux 下: +target_dir = '/Users/swa/backup' +# 要记得将这里的目录地址修改至你将使用的路径 + +# 如果目标目录还不存在,则进行创建 +if not os.path.exists(target_dir): + os.mkdir(target_dir) # 创建目录 + +# 3. 备份文件将打包压缩成 zip 文件。 +# 4. 将当前日期作为主备份目录下的 +# 子目录名称 +today = target_dir + os.sep + time.strftime('%Y%m%d') +# 将当前时间作为 zip 文件的文件名 +now = time.strftime('%H%M%S') + +# 添加一条来自用户的注释以创建 +# zip 文件的文件名 +comment = input('Enter a comment --> ') +# 检查是否有评论键入 +if len(comment) == 0: + target = today + os.sep + now + '.zip' +else: + target = today + os.sep + now + '_' + \ + comment.replace(' ', '_') + '.zip' + +# 如果子目录尚不存在则创建一个 +if not os.path.exists(today): + os.mkdir(today) + print('Successfully created directory', today) + +# 5. 我们使用 zip 命令将文件打包成 zip 格式 +zip_command = 'zip -r {0} {1}'.format(target, + ' '.join(source)) + +# 运行备份 +print('Zip command is:') +print(zip_command) +print('Running:') +if os.system(zip_command) == 0: + print('Successful backup to', target) +else: + print('Backup FAILED') + +``` 输出: -
{% include "./programs/backup_ver4.txt" %}
+```python +$ python backup_ver4.py +Enter a comment --> added new examples +Zip command is: +zip -r /Users/swa/backup/20140329/074122_added_new_examples.zip /Users/swa/notes +Running: + adding: Users/swa/notes/ (stored 0%) + adding: Users/swa/notes/blah1.txt (stored 0%) + adding: Users/swa/notes/blah2.txt (stored 0%) + adding: Users/swa/notes/blah3.txt (stored 0%) +Successful backup to /Users/swa/backup/20140329/074122_added_new_examples.zip + +``` **它是如何工作的** @@ -122,7 +378,7 @@ ## 继续改进 -第四版程序已经是一份对大多数用户来说都能令人满意地工作运行的脚本了,不过总会有改进的余地在。例如,你可以在程序中添加 `-v` 选项来指定程序的显示信息的_详尽_[^1]程度,从而使你的程序可以更具说服力,或者是添加 `-q` 选项使程序能_静默(Quiet)_运行。 +第四版程序已经是一份对大多数用户来说都能令人满意地工作运行的脚本了,不过总会有改进的余地在。例如,你可以在程序中添加 `-v` 选项来指定程序的显示信息的\_详尽\_[^1]程度,从而使你的程序可以更具说服力,或者是添加 `-q` 选项使程序能\_静默(Quiet)\_运行。 另一个可以增强的方向是在命令行中允许额外的文件与目录传递到脚本中。我们可以从 `sys.argv` 列表中获得这些名称,然后我们可以通过`list` 类提供的 `extend` 方法把它们添加到我们的 `source` 列表中. @@ -147,9 +403,7 @@ 要记住: -> 程序是成长起来的,不是搭建出来的。 -> (Software is grown, not built.) -> ——[Bill de hÓra](http://97things.oreilly.com/wiki/index.php/Great_software_is_not_built,_it_is_grown) +> 程序是成长起来的,不是搭建出来的。 (Software is grown, not built.) ——[Bill de hÓra](http://97things.oreilly.com/wiki/index.php/Great\_software\_is\_not\_built,\_it\_is\_grown) ## 总结 @@ -157,6 +411,6 @@ 接下来,我们将讨论面向对象编程。 +*** ----- [^1]: 原文作 Verbosity,沈洁元译本译作“交互”。 diff --git a/14.oop.md b/14.oop.md index cd467e2e..c04db0c9 100644 --- a/14.oop.md +++ b/14.oop.md @@ -1,52 +1,63 @@ -# 面向对象编程 {#oop} +# 面向对象编程 -在至今我们编写的所有程序中,我们曾围绕函数设计我们的程序,也就是那些能够处理数据的代码块。这被称作_面向过程(Procedure-oriented)_的编程方式。还有另外一种组织起你的程序的方式,它将数据与功能进行组合,并将其包装在被称作“对象”的东西内。在大多数情况下,你可以使用过程式编程,但是当你需要编写一个大型程序或面对某一更适合此方法的问题时,你可以考虑使用面向对象式的编程技术。 +在至今我们编写的所有程序中,我们曾围绕函数设计我们的程序,也就是那些能够处理数据的代码块。这被称作\_面向过程(Procedure-oriented)\_的编程方式。还有另外一种组织起你的程序的方式,它将数据与功能进行组合,并将其包装在被称作“对象”的东西内。在大多数情况下,你可以使用过程式编程,但是当你需要编写一个大型程序或面对某一更适合此方法的问题时,你可以考虑使用面向对象式的编程技术。 -类与对象是面向对象编程的两个主要方面。一个**类(Class)**能够创建一种新的_类型(Type)_,其中**对象(Object)**就是类的**实例(Instance)**。可以这样来类比:你可以拥有类型 `int` 的变量,也就是说存储整数的变量是 `int` 类的实例(对象)。 +类与对象是面向对象编程的两个主要方面。一个**类(Class)能够创建一种新的\_类型(Type)\_,其中对象(Object)就是类的实例(Instance)**。可以这样来类比:你可以拥有类型 `int` 的变量,也就是说存储整数的变量是 `int` 类的实例(对象)。 > **针对静态编程语言程序员的提示** -> -> 请注意,即使是整数也会被视为对象(`int` 类的对象)。这不同于 C++ 与 Java(1.5 版之前),在它们那儿整数是原始内置类型。[^1] -> +> +> 请注意,即使是整数也会被视为对象(`int` 类的对象)。这不同于 C++ 与 Java(1.5 版之前),在它们那儿整数是原始内置类型。 +> > 有关类的更多详细信息,请参阅 `help(int)`。 -> -> C# 与 Java 1.5 程序员会发现这与_装箱与拆箱(Boxing and Unboxing)_概念[^2]颇有相似之处。 +> +> C# 与 Java 1.5 程序员会发现这与_装箱与拆箱(Boxing and Unboxing)_概念颇有相似之处。 -对象可以使用_属于_它的普通变量来存储数据。这种从属于对象或类的变量叫作**字段(Field)**。对象还可以使用_属于_类的函数来实现某些功能,这种函数叫作类的**方法(Method)**。这两个术语很重要,它有助于我们区分函数与变量,哪些是独立的,哪些又是属于类或对象的。总之,字段与方法通称类的**属性(Attribute)**。 +对象可以使用\_属于\_它的普通变量来存储数据。这种从属于对象或类的变量叫作**字段(Field)**。对象还可以使用\_属于\_类的函数来实现某些功能,这种函数叫作类的**方法(Method)**。这两个术语很重要,它有助于我们区分函数与变量,哪些是独立的,哪些又是属于类或对象的。总之,字段与方法通称类的**属性(Attribute)**。 -字段有两种类型——它们属于某一类的各个实例或对象,或是从属于某一类本身。它们被分别称作**实例变量(Instance Variables)**与**类变量(Class Variables)**。 +字段有两种类型——它们属于某一类的各个实例或对象,或是从属于某一类本身。它们被分别称作**实例变量(Instance Variables)与类变量(Class Variables)**。 通过 `class` 关键字可以创建一个类。这个类的字段与方法可以在缩进代码块中予以列出。 -## `self` {#self} +## `self` -类方法与普通函数只有一种特定的区别——前者必须多加一个参数在参数列表开头,这个名字必须添加到参数列表的开头,但是你*不用*在你调用这个功能时为这个参数赋值,Python 会为它提供。这种特定的变量引用的是对象_本身_,按照惯例,它被赋予 `self` 这一名称。 +类方法与普通函数只有一种特定的区别——前者必须多加一个参数在参数列表开头,这个名字必须添加到参数列表的开头,但是你_不用_在你调用这个功能时为这个参数赋值,Python 会为它提供。这种特定的变量引用的是对象\_本身\_,按照惯例,它被赋予 `self` 这一名称。 -尽管你可以为这一参数赋予任何名称,但是_强烈推荐_你使用 `self` 这一名称——其它的任何一种名称绝对会引人皱眉。使用一个标准名称能带来诸多好处——任何一位你的程序的读者能够立即认出它,甚至是专门的 IDE(Integrated Development Environments,集成开发环境)也可以为你提供帮助,只要你使用了 `self` 这一名称。 +尽管你可以为这一参数赋予任何名称,但是\_强烈推荐\_你使用 `self` 这一名称——其它的任何一种名称绝对会引人皱眉。使用一个标准名称能带来诸多好处——任何一位你的程序的读者能够立即认出它,甚至是专门的 IDE(Integrated Development Environments,集成开发环境)也可以为你提供帮助,只要你使用了 `self` 这一名称。 > **针对 C++/Java/C# 程序员的提示** -> +> > Python 中的 `self` 相当于 C++ 中的 `this` 指针以及 Java 与 C# 中的 `this` 引用。 你一定会在想 Python 是如何给 `self` 赋值的,以及为什么你不必给它一个值。一个例子或许会让这些疑问得到解答。假设你有一个 `MyClass` 的类,这个类下有一个实例 `myobject`。当你调用一个这个对象的方法,如 `myobject.method(arg1, arg2)` 时,Python 将会自动将其转换成 `MyClass.method(myobject, arg1, arg2)`——这就是 `self` 的全部特殊之处所在。 这同时意味着,如果你有一个没有参数的方法,你依旧必须拥有一个参数——`self`。 -## 类 {#class} +## 类 最简单的类(Class)可以通过下面的案例来展示(保存为 `oop_simplestclass.py`): -
{% include "./programs/oop_simplestclass.py" %}
+```python +class Person: + pass # 一个空的代码块 + +p = Person() +print(p) + +``` 输出: -
{% include "./programs/oop_simplestclass.txt" %}
+```python +$ python oop_simplestclass.py +<__main__.Person instance at 0x10171f518> + +``` **它是如何工作的** 我们通过使用 `class` 语句与这个类的名称来创建一个新类。在它之后是一个缩进的语句块,代表这个类的主体。在本案例中,我们创建的是一个空代码块,使用 `pass` 语句予以标明。 -然后,我们通过采用类的名称后跟一对括号的方法,给这个类创建一个对象(或是实例,我们将在[后面的章节](#init)中了解有关实例的更多内容)。为了验证我们的操作是否成功,我们通过直接将它们打印出来来确认变量的类型。结果告诉我们我们在 `Person` 类的 `__main__` 模块中拥有了一个实例。 +然后,我们通过采用类的名称后跟一对括号的方法,给这个类创建一个对象(或是实例,我们将在[后面的章节](14.oop.md#init)中了解有关实例的更多内容)。为了验证我们的操作是否成功,我们通过直接将它们打印出来来确认变量的类型。结果告诉我们我们在 `Person` 类的 `__main__` 模块中拥有了一个实例。 要注意到在本例中还会打印出计算机内存中存储你的对象的地址。案例中给出的地址会与你在你的电脑上所能看见的地址不相同,因为 Python 会在它找到的任何空间来存储对象。 @@ -54,29 +65,58 @@ 我们已经在前面讨论过类与对象一如函数那般都可以带有方法(Method),唯一的不同在于我们还拥有一个额外的 `self` 变量。现在让我们来看看下面的例子(保存为 `oop_method.py`)。 -
{% include "./programs/oop_method.py" %}
+```python +class Person: + def say_hi(self): + print('Hello, how are you?') + +p = Person() +p.say_hi() +# 前面两行同样可以写作 +# Person().say_hi() +``` 输出: -
{% include "./programs/oop_method.txt" %}
+``` +$ python oop_method.py +Hello, how are you? + +``` **它是如何工作的** 这里我们就能看见 `self` 是如何行动的了。要注意到 `say_hi` 这一方法不需要参数,但是依旧在函数定义中拥有 `self` 变量。 -## `__init__` 方法 {#init} +## `__init__` 方法 在 Python 的类中,有不少方法的名称具有着特殊的意义。现在我们要了解的就是 `__init__` 方法的意义。 -`__init__` 方法会在类的对象被实例化(Instantiated)时立即运行。这一方法可以对任何你想进行操作的目标对象进行*初始化(Initialization)*操作。这里你要注意在 init 前后加上的双下划线。 +`__init__` 方法会在类的对象被实例化(Instantiated)时立即运行。这一方法可以对任何你想进行操作的目标对象进行\*初始化(Initialization)\*操作。这里你要注意在 init 前后加上的双下划线。 案例(保存为 `oop_init.py`): -
{% include "./programs/oop_init.py" %}
+```python +class Person: + def __init__(self, name): + self.name = name + + def say_hi(self): + print('Hello, my name is', self.name) + +p = Person('Swaroop') +p.say_hi() +# 前面两行同时也能写作 +# Person('Swaroop').say_hi() +``` 输出: -
{% include "./programs/oop_init.txt" %}
+``` +$ python oop_init.py +Hello, my name is Swaroop + +``` **它是如何工作的** @@ -84,26 +124,101 @@ 当我们在 `Person` 类下创建新的实例 `p` 时,我们采用的方法是先写下类的名称,后跟括在括号中的参数,形如:`p = Person('Swaroop')`。 -我们不会显式地调用 `__init__` 方法。 -这正是这个方法的特殊之处所在。 +我们不会显式地调用 `__init__` 方法。 这正是这个方法的特殊之处所在。 现在,我们可以使用我们方法中的 `self.name` 字段了,使用的方法在 `say_hi` 方法中已经作过说明。 -## 类变量与对象变量 {#class-obj-vars}[^3] +## 类变量与对象变量 {#class-obj-vars} -我们已经讨论过了类与对象的功能部分(即方法),现在让我们来学习它们的数据部分。数据部分——也就是字段——只不过是_绑定(Bound)_到类与对象的**命名空间(Namespace)**的普通变量。这就代表着这些名称仅在这些类与对象所存在的上下文中有效。这就是它们被称作_“命名空间”_的原因。 +我们已经讨论过了类与对象的功能部分(即方法),现在让我们来学习它们的数据部分。数据部分——也就是字段——只不过是\_绑定(Bound)_到类与对象的\*\*命名空间(Namespace)\*\*的普通变量。这就代表着这些名称仅在这些类与对象所存在的上下文中有效。这就是它们被称作_“命名空间”\_的原因。 -_字段(Field)_有两种类型——类变量与对象变量,它们根据究竟是类还是对象_拥有_这些变量来进行分类。 +\_字段(Field)\_有两种类型——类变量与对象变量,它们根据究竟是类还是对象\_拥有\_这些变量来进行分类。 **类变量(Class Variable)**是共享的(Shared)——它们可以被属于该类的所有实例访问。该类变量只拥有一个副本,当任何一个对象对类变量作出改变时,发生的变动将在其它所有实例中都会得到体现。 **对象变量(Object variable)**由类的每一个独立的对象或实例所拥有。在这种情况下,每个对象都拥有属于它自己的字段的副本,也就是说,它们不会被共享,也不会以任何方式与其它不同实例中的相同名称的字段产生关联。下面一个例子可以帮助你理解(保存为 `oop_objvar.py`): -
{% include "./programs/oop_objvar.py" %}
+```python +# coding=UTF-8 + +class Robot: + """表示有一个带有名字的机器人。""" + + # 一个类变量,用来计数机器人的数量 + population = 0 + + def __init__(self, name): + """初始化数据""" + self.name = name + print("(Initializing {})".format(self.name)) + + # 当有人被创建时,机器人 + # 将会增加人口数量 + Robot.population += 1 + + def die(self): + """我挂了。""" + print("{} is being destroyed!".format(self.name)) + + Robot.population -= 1 + + if Robot.population == 0: + print("{} was the last one.".format(self.name)) + else: + print("There are still {:d} robots working.".format( + Robot.population)) + + def say_hi(self): + """来自机器人的诚挚问候 + + 没问题,你做得到。""" + print("Greetings, my masters call me {}.".format(self.name)) + + @classmethod + def how_many(cls): + """打印出当前的人口数量""" + print("We have {:d} robots.".format(cls.population)) + + +droid1 = Robot("R2-D2") +droid1.say_hi() +Robot.how_many() + +droid2 = Robot("C-3PO") +droid2.say_hi() +Robot.how_many() + +print("\nRobots can do some work here.\n") + +print("Robots have finished their work. So let's destroy them.") +droid1.die() +droid2.die() + +Robot.how_many() + +``` 输出: -
{% include "./programs/oop_objvar.txt" %}
+```python +$ python oop_objvar.py +(Initializing R2-D2) +Greetings, my masters call me R2-D2. +We have 1 robots. +(Initializing C-3PO) +Greetings, my masters call me C-3PO. +We have 2 robots. + +Robots can do some work here. + +Robots have finished their work. So let's destroy them. +R2-D2 is being destroyed! +There are still 1 robots working. +C-3PO is being destroyed! +C-3PO was the last one. +We have 0 robots. + +``` **它是如何工作的** @@ -115,7 +230,7 @@ _字段(Field)_有两种类型——类变量与对象变量,它们根据 `how_many` 实际上是一个属于类而非属于对象的方法。这就意味着我们可以将它定义为一个 `classmethod(类方法)` 或是一个 `staticmethod(静态方法)`,这取决于我们是否需要知道这一方法属于哪个类。由于我们已经引用了一个类变量,因此我们使用 `classmethod(类方法)`。 -我们使用[装饰器(Decorator)](./18.more.md#decorator)将 `how_many` 方法标记为类方法。 +我们使用[装饰器(Decorator)](18.more.md#decorator)将 `how_many` 方法标记为类方法。 你可以将装饰器想象为调用一个包装器(Wrapper)函数的快捷方式,因此启用 `@classmethod` 装饰器等价于调用: @@ -125,59 +240,116 @@ how_many = classmethod(how_many) 你会观察到 `__init__` 方法会使用一个名字以初始化 `Robot` 实例。在这一方法中,我们将 `population` 按 1 往上增长,因为我们多增加了一台机器人。你还会观察到 `self.name` 的值是指定给每个对象的,这体现了对象变量的本质。 -你需要记住你*只能*使用 `self` 来引用同一对象的变量与方法。这被称作*属性引用(Attribute Reference)*。 +你需要记住你_只能_使用 `self` 来引用同一对象的变量与方法。这被称作_属性引用(Attribute Reference)_。 -在本程序中,我们还会看见针对类和方法的 *文档字符串(DocStrings)* 的使用方式。我们可以在运行时通过 `Robot.__doc__` 访问类的 文档字符串,对于方法的文档字符串,则可以使用 `Robot.say_hi.__doc__`。 +在本程序中,我们还会看见针对类和方法的 _文档字符串(DocStrings)_ 的使用方式。我们可以在运行时通过 `Robot.__doc__` 访问类的 文档字符串,对于方法的文档字符串,则可以使用 `Robot.say_hi.__doc__`。 在 `die` 方法中,我们简单地将 `Robot.population` 的计数按 1 向下减少。 -所有的类成员都是公开的。但有一个例外:如果你使用数据成员并在其名字中_使用双下划线作为前缀_,形成诸如 `__privatevar` 这样的形式,Python 会使用名称调整(Name-mangling)来使其有效地成为一个私有变量。 +所有的类成员都是公开的。但有一个例外:如果你使用数据成员并在其名字中\_使用双下划线作为前缀\_,形成诸如 `__privatevar` 这样的形式,Python 会使用名称调整(Name-mangling)来使其有效地成为一个私有变量。 因此,你需要遵循这样的约定:任何在类或对象之中使用的变量其命名应以下划线开头,其它所有非此格式的名称都将是公开的,并可以为其它任何类或对象所使用。请记得这只是一个约定,Python 并不强制如此(除了双下划线前缀这点)。 > **针对 C++/Java/C# 程序员的提示** -> -> 所有类成员(包括数据成员)都是_公开的_,并且 Python 中所有的方法都是_虚拟的(Virtual)_。 +> +> 所有类成员(包括数据成员)都是\_公开的\_,并且 Python 中所有的方法都是\_虚拟的(Virtual)\_。 ## 继承 -面向对象编程的一大优点是对代码的**重用(Reuse)**,重用的一种实现方法就是通过**继承(Inheritance)**机制。继承最好是想象成在类之间实现**类型与子类型(Type and Subtype)**关系的工具。 +面向对象编程的一大优点是对代码的**重用(Reuse)**,重用的一种实现方法就是通过\*\*继承(Inheritance)**机制。继承最好是想象成在类之间实现**类型与子类型(Type and Subtype)\*\*关系的工具。 现在假设你希望编写一款程序来追踪一所大学里的老师和学生。有一些特征是他们都具有的,例如姓名、年龄和地址。另外一些特征是他们独有的,一如教师的薪水、课程与假期,学生的成绩和学费。 你可以为每一种类型创建两个独立的类,并对它们进行处理。但增添一条共有特征就意味着将其添加进两个独立的类。这很快就会使程序变得笨重。 -一个更好的方法是创建一个公共类叫作 `SchoolMember`,然后让教师和学生从这个类中_继承(Inherit)_,也就是说他们将成为这一类型(类)的子类型,而我们就可以向这些子类型中添加某些该类独有的特征。 +一个更好的方法是创建一个公共类叫作 `SchoolMember`,然后让教师和学生从这个类中\_继承(Inherit)\_,也就是说他们将成为这一类型(类)的子类型,而我们就可以向这些子类型中添加某些该类独有的特征。 这种方法有诸多优点。如果我们增加或修改了 `SchoolMember` 的任何功能,它将自动反映在子类型中。举个例子,你可以通过简单地向 SchoolMember 类进行操作,来为所有老师与学生添加一条新的 ID 卡字段。不过,对某一子类型作出的改动并不会影响到其它子类型。另一大优点是你可以将某一老师或学生对象看作 `SchoolMember` 的对象并加以引用,这在某些情况下会大为有用,例如清点学校中的成员数量。这被称作**多态性(Polymorphism)**,在任何情况下,如果父类型希望,子类型都可以被替换,也就是说,该对象可以被看作父类的实例。 同时还需要注意的是我们重用父类的代码,但我们不需要再在其它类中重复它们,当我们使用独立类型时才会必要地重复这些代码。 -在上文设想的情况中,`SchoolMember` 类会被称作**基类(Base Class)**[^4]或是**超类(Superclass)**。`Teacher` 和 `Student` 类会被称作**派生类(Derived Classes)**[^5]或是**子类(Subclass)**。 +在上文设想的情况中,`SchoolMember` 类会被称作**基类(Base Class)**或是**超类(Superclass)**。`Teacher` 和 `Student` 类会被称作**派生类(Derived Classes)**或是**子类(Subclass)**。 我们将通过下面的程序作为案例来进行了解(保存为 `oop_subclass.py`): -
{% include "./programs/oop_subclass.py" %}
+```python +# coding=UTF-8 + +class SchoolMember: + '''代表任何学校里的成员。''' + def __init__(self, name, age): + self.name = name + self.age = age + print('(Initialized SchoolMember: {})'.format(self.name)) + + def tell(self): + '''告诉我有关我的细节。''' + print('Name:"{}" Age:"{}"'.format(self.name, self.age), end=" ") + + +class Teacher(SchoolMember): + '''代表一位老师。''' + def __init__(self, name, age, salary): + SchoolMember.__init__(self, name, age) + self.salary = salary + print('(Initialized Teacher: {})'.format(self.name)) + + def tell(self): + SchoolMember.tell(self) + print('Salary: "{:d}"'.format(self.salary)) + + +class Student(SchoolMember): + '''代表一位学生。''' + def __init__(self, name, age, marks): + SchoolMember.__init__(self, name, age) + self.marks = marks + print('(Initialized Student: {})'.format(self.name)) + + def tell(self): + SchoolMember.tell(self) + print('Marks: "{:d}"'.format(self.marks)) + +t = Teacher('Mrs. Shrividya', 40, 30000) +s = Student('Swaroop', 25, 75) + +# 打印一行空白行 +print() + +members = [t, s] +for member in members: + # 对全体师生工作 + member.tell() +``` 输出: -
{% include "./programs/oop_subclass.txt" %}
+```python +$ python oop_subclass.py +(Initialized SchoolMember: Mrs. Shrividya) +(Initialized Teacher: Mrs. Shrividya) +(Initialized SchoolMember: Swaroop) +(Initialized Student: Swaroop) + +Name:"Mrs. Shrividya" Age:"40" Salary: "30000" +Name:"Swaroop" Age:"25" Marks: "75" +``` **它是如何工作的** -要想使用继承,在定义类[^6]时我们需要在类后面跟一个包含基类名称的元组。然后,我们会注意到基类的 `__init__` 方法是通过 `self` 变量被显式调用的,因此我们可以初始化对象的基类部分。下面这一点很重要,需要牢记——因为我们在 `Teacher` 和 `Student` 子类中定义了 `__init__` 方法,Python 不会自动调用基类 `SchoolMember` 的构造函数,你必须自己显式地调用它。 +要想使用继承,在定义类时我们需要在类后面跟一个包含基类名称的元组。然后,我们会注意到基类的 `__init__` 方法是通过 `self` 变量被显式调用的,因此我们可以初始化对象的基类部分。下面这一点很重要,需要牢记——因为我们在 `Teacher` 和 `Student` 子类中定义了 `__init__` 方法,Python 不会自动调用基类 `SchoolMember` 的构造函数,你必须自己显式地调用它。 -相反,如果我们_没有_在一个子类中定义一个 `__init__` 方法,Python 将会自动调用基类的构造函数。 +相反,如果我们\_没有\_在一个子类中定义一个 `__init__` 方法,Python 将会自动调用基类的构造函数。 我们会观察到,我们可以通过在方法名前面加上基类名作为前缀,再传入 `self` 和其余变量,来调用基类的方法。 在这里你需要注意,当我们使用 `SchoolMember` 类的 `tell` 方法时,我们可以将 `Teacher` 或 `Student` 的实例看作 `SchoolMember` 的实例。 -同时,你会发现被调用的是子类型的 `tell` 方法,而不是 `SchoolMember` 的 `tell` 方法。理解这一问题的一种思路是 Python *总会*从当前的实际类型中开始寻找方法,在本例中即是如此。如果它找不到对应的方法,它就会在该类所属的基本类中依顺序逐个寻找属于基本类的方法,这个基本类是在定义子类时后跟的元组指定的。 +同时,你会发现被调用的是子类型的 `tell` 方法,而不是 `SchoolMember` 的 `tell` 方法。理解这一问题的一种思路是 Python _总会_从当前的实际类型中开始寻找方法,在本例中即是如此。如果它找不到对应的方法,它就会在该类所属的基本类中依顺序逐个寻找属于基本类的方法,这个基本类是在定义子类时后跟的元组指定的。 这里有一条有关术语的注释——如果继承元组(Inheritance Tuple)中有超过一个类,这种情况就会被称作**多重继承(Multiple Inheritance)**。 -`end` 参数用在超类的 `tell()` 方法的 `print` 函数中,目的是打印一行并允许下一次打印在同一行继续。这是一个让 `print` 能够不在打印的末尾打印出 `\n` (新行换行符)符号的小窍门。 +`end` 参数用在超类的 `tell()` 方法的 `print` 函数中,目的是打印一行并允许下一次打印在同一行继续。这是一个让 `print` 能够不在打印的末尾打印出 (新行换行符)符号的小窍门。 ## 总结 @@ -185,17 +357,8 @@ how_many = classmethod(how_many) 接下来,我们将学习如何处理输入与输出,以及如何在 Python 中访问文件。 +*** - ----- -[^1]: 原文作 Primitive native types,沈洁元译本表达为“把整数纯粹作为类型”。Primitive type 翻译作“原始类型”,也称作“内置类型”,因此此处也可以翻译成“基本内置类型”。 - -[^2]: 沈洁元译本译作“封装与解封装”。 - -[^3]: 本节标题原文作 Class And Object Variables,沈洁元译本译作“类与对象的方法”。 - -[^4]: 沈洁元译本译作“基本类”。 - -[^5]: 沈洁元译本译作“导出类”。 - -[^6]: 此处的类即派生类或子类。 +1. 沈洁元译本译作“封装与解封装”。 +2. 沈洁元译本译作“基本类”。 +3. 此处的类即派生类或子类。 diff --git a/15.io.md b/15.io.md index c1285f34..78b00f56 100644 --- a/15.io.md +++ b/15.io.md @@ -1,4 +1,4 @@ -# 输入与输出 {#io} +# 输入与输出 有些时候你的程序会与用户产生交互。举个例子,你会希望获取用户的输入内容,并向用户打印出一些返回的结果。我们可以分别通过 `input()` 函数与 `print` 函数来实现这一需求。 @@ -10,15 +10,43 @@ 将以下程序保存为 `io_input.py`: -
{% include "./programs/io_input.py" %}
+```python +def reverse(text): + return text[::-1] + + +def is_palindrome(text): + return text == reverse(text) + + +something = input("Enter text: ") +if is_palindrome(something): + print("Yes, it is a palindrome") +else: + print("No, it is not a palindrome") + +``` 输出: -
{% include "./programs/io_input.txt" %}
+```python +$ python3 io_input.py +Enter text: sir +No, it is not a palindrome + +$ python3 io_input.py +Enter text: madam +Yes, it is a palindrome + +$ python3 io_input.py +Enter text: racecar +Yes, it is a palindrome + +``` **它是如何工作的** -我们使用切片功能翻转文本。我们已经了解了我们可以通过使用 `seq[a:b]` 来从位置 `a` 开始到位置 `b` 结束来[对序列进行切片](./12.data_structures.md#sequence) 。我们同样可以提供第三个参数来确定切片的_步长(Step)_。默认的步长为 `1`,它会返回一份连续的文本。如果给定一个负数步长,如 `-1`,将返回翻转过的文本。 +我们使用切片功能翻转文本。我们已经了解了我们可以通过使用 `seq[a:b]` 来从位置 `a` 开始到位置 `b` 结束来[对序列进行切片](12.data\_structures.md#sequence) 。我们同样可以提供第三个参数来确定切片的\_步长(Step)\_。默认的步长为 `1`,它会返回一份连续的文本。如果给定一个负数步长,如 `-1`,将返回翻转过的文本。 `input()` 函数可以接受一个字符串作为参数,并将其展示给用户。尔后它将等待用户输入内容或敲击返回键。一旦用户输入了某些内容并敲下返回键,`input()` 函数将返回用户输入的文本。 @@ -28,7 +56,7 @@ 要想检查文本是否属于回文需要忽略其中的标点、空格与大小写。例如,“Rise to vote, sir.”是一段回文文本,但是我们现有的程序不会这么认为。你可以改进上面的程序以使它能够识别这段回文吗? -如果你需要一些提示,那么这里有一个想法……[^1] +如果你需要一些提示,那么这里有一个想法…… ## 文件 @@ -36,11 +64,48 @@ 案例(保存为 `io_using_file.py`): -
{% include "./programs/io_using_file.py" %}
+```python +poem = '''\ +Programming is fun +When the work is done +if you wanna make your work also fun: + use Python! +''' + +# 打开文件以编辑('w'riting) +f = open('poem.txt', 'w') +# 向文件中编写文本 +f.write(poem) +# 关闭文件 +f.close() + +# 如果没有特别指定, +# 将假定启用默认的阅读('r'ead)模式 +f = open('poem.txt') +while True: + line = f.readline() + # 零长度指示 EOF + if len(line) == 0: + break + # 每行(`line`)的末尾 + # 都已经有了换行符 + #因为它是从一个文件中进行读取的 + print(line, end='') +# 关闭文件 +f.close() + +``` 输出: -
{% include "./programs/io_using_file.txt" %}
+``` +$ python3 io_using_file.py +Programming is fun +When the work is done +if you wanna make your work also fun: + use Python! + +``` **它是如何工作的** @@ -48,31 +113,58 @@ 在我们的案例中,我们首先采用写入模式打开文件并使用文件对象的 `write` 方法来写入文件,并在最后通过 `close` 关闭文件。 -接下来,我们重新在阅读模式下打开同一个文件。我们不需要特别指定某种模式,因为“阅读文本文件”是默认的。我们在循环中使用 `readline` 方法来读取文件的每一行。这一方法将会一串完整的行,其中在行末尾还包含了换行符。当一个_空_字符串返回时,它表示我们已经到达了文件末尾,并且通过 `break` 退出循环。 +接下来,我们重新在阅读模式下打开同一个文件。我们不需要特别指定某种模式,因为“阅读文本文件”是默认的。我们在循环中使用 `readline` 方法来读取文件的每一行。这一方法将会一串完整的行,其中在行末尾还包含了换行符。当一个\_空\_字符串返回时,它表示我们已经到达了文件末尾,并且通过 `break` 退出循环。 最后,我们通过 `close` 关闭了文件。 现在,你可以检查 `poem.txt` 文件的内容来确认程序确实对该文件进行了写入与读取操作。 -## Pickle[^2] +## Pickle -Python 提供了一个叫作 `Pickle` 的标准模块,通过它你可以将_任何_纯 Python 对象存储到一个文件中,并在稍后将其取回。这叫作*持久地(Persistently)*存储对象。 +Python 提供了一个叫作 `Pickle` 的标准模块,通过它你可以将\_任何\_纯 Python 对象存储到一个文件中,并在稍后将其取回。这叫作\*持久地(Persistently)\*存储对象。 案例(保存为 `io_pickle.py`): -
{% include "./programs/io_pickle.py" %}
+```python +import pickle + +# 我们存储相关对象的文件的名称 +shoplistfile = 'shoplist.data' +# 需要购买的物品清单 +shoplist = ['apple', 'mango', 'carrot'] + +# 准备写入文件 +f = open(shoplistfile, 'wb') +# 转储对象至文件 +pickle.dump(shoplist, f) +f.close() + +# 清除 shoplist 变量 +del shoplist + +# 重新打开存储文件 +f = open(shoplistfile, 'rb') +# 从文件中载入对象 +storedlist = pickle.load(f) +print(storedlist) + +``` 输出: -
{% include "./programs/io_pickle.txt" %}
+``` +$ python io_pickle.py +['apple', 'mango', 'carrot'] + +``` **它是如何工作的** -要想将一个对象存储到一个文件中,我们首先需要通过 `open` 以写入(**w**rite)二进制(**b**inary)模式打开文件,然后调用 `pickle` 模块的 `dump` 函数。这一过程被称作_封装(Pickling)_。 +要想将一个对象存储到一个文件中,我们首先需要通过 `open` 以写入(**w**rite)二进制(**b**inary)模式打开文件,然后调用 `pickle` 模块的 `dump` 函数。这一过程被称作\_封装(Pickling)\_。 -接着,我们通过 `pickle` 模块的 `load` 函数接收返回的对象。这个过程被称作_拆封(Unpickling)_。 +接着,我们通过 `pickle` 模块的 `load` 函数接收返回的对象。这个过程被称作\_拆封(Unpickling)\_。 -## Unicode[^3] +## Unicode[^1] 截止到现在,当我们编写或使用字符串、读取或写入某一文件时,我们用到的只是简单的英语字符。 @@ -91,21 +183,32 @@ Python 提供了一个叫作 `Pickle` 的标准模块,通过它你可以将_ 当我们阅读或写入某一文件或当我们希望与互联网上的其它计算机通信时,我们需要将我们的 Unicode 字符串转换至一个能够被发送和接收的格式,这个格式叫作“UTF-8”。我们可以在这一格式下进行读取与写入,只需使用一个简单的关键字参数到我们的标准 `open` 函数中: -
{% include "./programs/io_unicode.py" %}
+```python +# encoding=utf-8 +import io + +f = io.open("abc.txt", "wt", encoding="utf-8") +f.write(u"Imagine non-English language here") +f.close() + +text = io.open("abc.txt", encoding="utf-8").read() +print(text) + +``` **它是如何工作的** -现在你可以忽略 `import` 语句,我们会在[模块章节](./11.modules.md#modules)章节探讨有关它的更多细节。 +现在你可以忽略 `import` 语句,我们会在[模块章节](11.modules.md#modules)章节探讨有关它的更多细节。 -每当我们诸如上面那番使用 Unicode 字面量编写一款程序时,我们必须确保 Python 程序已经被告知我们使用的是 UTF-8,因此我们必须将 `# encoding=utf-8` 这一注释放置在我们程序的顶端。[^4] +每当我们诸如上面那番使用 Unicode 字面量编写一款程序时,我们必须确保 Python 程序已经被告知我们使用的是 UTF-8,因此我们必须将 `# encoding=utf-8` 这一注释放置在我们程序的顶端。 我们使用 `io.open` 并提供了“编码(Encoding)”与“解码(Decoding)”参数来告诉 Python 我们正在使用 Unicode。 你可以阅读以下文章来了解有关这一话题的更多内容: -- ["The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets"](http://www.joelonsoftware.com/articles/Unicode.html) -- [Python Unicode Howto](http://docs.python.org/3/howto/unicode.html) -- [Pragmatic Unicode talk by Nat Batchelder](http://nedbatchelder.com/text/unipain.html) +* ["The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets"](http://www.joelonsoftware.com/articles/Unicode.html) +* [Python Unicode Howto](http://docs.python.org/3/howto/unicode.html) +* [Pragmatic Unicode talk by Nat Batchelder](http://nedbatchelder.com/text/unipain.html) ## 总结 @@ -113,12 +216,9 @@ Python 提供了一个叫作 `Pickle` 的标准模块,通过它你可以将_ 接下来,我们将探索一些异常的概念。 ---- - -[^1]: 使用一个元组(你可以在这里找到一份列出_所有_[标点符号](http://grammar.ccc.commnet.edu/grammar/marks/marks.htm)的列表)来保存所有需要禁用的字符,然后使用成员资格测试来确定一个字符是否应该被移除,即 forbidden = (`!`, `?`, `.`, ...)。——原书注 - -[^2]: 沈洁元译本将本节标题译作“储存器”,两版原书在本节的标题相同,但是内容已大不相同。 +*** -[^3]: Unicode 有“统一码”“万国码”“国际码”等多种译名。出于交流习惯的考虑,此处全部采用原文。 +1. 沈洁元译本将本节标题译作“储存器”,两版原书在本节的标题相同,但是内容已大不相同。 +2. 可能你已经注意到,在前面章节的一些程序文件中开头标注了采用 UTF-8 编码。这是在中文版翻译过程中为了修改程序中使用三引号括起的说明性字符串,同时要保证程序代码能被 Python 正常识别而作出的改动。 -[^4]: 可能你已经注意到,在前面章节的一些程序文件中开头标注了采用 UTF-8 编码。这是在中文版翻译过程中为了修改程序中使用三引号括起的说明性字符串,同时要保证程序代码能被 Python 正常识别而作出的改动。 \ No newline at end of file +[^1]: Unicode 有“统一码”“万国码”“国际码”等多种译名。出于交流习惯的考虑,此处全部采用原文。 diff --git a/16.exceptions.md b/16.exceptions.md index 0da63c95..c2ef3d15 100644 --- a/16.exceptions.md +++ b/16.exceptions.md @@ -2,11 +2,11 @@ 当你的程序出现例外情况时就会发生异常(Exception)。例如,当你想要读取一个文件时,而那个文件却不存在,怎么办?又或者你在程序执行时不小心把它删除了,怎么办?这些通过使用**异常**来进行处理。 -类似地,如果你的程序中出现了一些无效的语句该怎么办?Python 将会对此进行处理,**举起(Raises)**[^1]它的小手来告诉你哪里出现了一个**错误(Error)**。 +类似地,如果你的程序中出现了一些无效的语句该怎么办?Python 将会对此进行处理,**举起(Raises)**它的小手来告诉你哪里出现了一个**错误(Error)**。 ## 错误 -你可以想象一个简单的 `print` 函数调用。如果我们把 `print` 误拼成 `Print` 会怎样?你会注意到它的首字母是大写。在这一例子中,Python 会_抛出(Raise)_一个语法错误。 +你可以想象一个简单的 `print` 函数调用。如果我们把 `print` 误拼成 `Print` 会怎样?你会注意到它的首字母是大写。在这一例子中,Python 会\_抛出(Raise)\_一个语法错误。 ```python >>> Print("Hello World") @@ -17,7 +17,7 @@ NameError: name 'Print' is not defined Hello World ``` -你会注意到一个 `NameError` 错误被抛出,同时 Python 还会打印出检测到的错误发生的位置。这就是一个错误**错误处理器(Error Handler)**[^2] 为这个错误所做的事情。 +你会注意到一个 `NameError` 错误被抛出,同时 Python 还会打印出检测到的错误发生的位置。这就是一个错误**错误处理器(Error Handler)** 为这个错误所做的事情。 ## 异常 @@ -30,7 +30,7 @@ Enter something --> Traceback (most recent call last): EOFError ``` -此处 Python 指出了一个称作 `EOFError` 的错误,代表着它发现了一个*文件结尾(End of File)*符号(由 `ctrl-d` 实现)在不该出现的时候出现了。 +此处 Python 指出了一个称作 `EOFError` 的错误,代表着它发现了一个\*文件结尾(End of File)\*符号(由 `ctrl-d` 实现)在不该出现的时候出现了。 ## 处理异常 @@ -38,15 +38,38 @@ EOFError 案例(保存文 `exceptions_handle.py`): -
{% include "./programs/exceptions_handle.py" %}
+```python +try: + text = input('Enter something --> ') +except EOFError: + print('Why did you do an EOF on me?') +except KeyboardInterrupt: + print('You cancelled the operation.') +else: + print('You entered {}'.format(text)) + +``` 输出: -
{% include "./programs/exceptions_handle.txt" %}
+``` +# Press ctrl + d +$ python exceptions_handle.py +Enter something --> Why did you do an EOF on me? + +# Press ctrl + c +$ python exceptions_handle.py +Enter something --> ^CYou cancelled the operation. + +$ python exceptions_handle.py +Enter something --> No exceptions +You entered No exceptions + +``` **它是如何工作的** -我们将所有可能引发异常或错误的语句放在 `try` 代码块中,并将相应的错误或异常的处理器(Handler)放在 `except` 子句或代码块中。`except` 子句可以处理某种特定的错误或异常,或者是一个在括号中列出的错误或异常。如果没有提供错误或异常的名称,它将处理_所有_错误与异常。 +我们将所有可能引发异常或错误的语句放在 `try` 代码块中,并将相应的错误或异常的处理器(Handler)放在 `except` 子句或代码块中。`except` 子句可以处理某种特定的错误或异常,或者是一个在括号中列出的错误或异常。如果没有提供错误或异常的名称,它将处理\_所有\_错误与异常。 要注意到必须至少有一句 `except` 字句与每一句 `try` 字句相关联。不然,有一个 try 代码块又有什么意义? @@ -56,19 +79,52 @@ EOFError 在下一个案例中,我们还将了解如何获取异常对象以便我们可以检索其他信息。 -## 抛出异常[^3] +## 抛出异常 -你可以通过 `raise` 语句来_引发_一次异常,具体方法是提供错误名或异常名以及要_抛出(Thrown)_异常的对象。 +你可以通过 `raise` 语句来\_引发\_一次异常,具体方法是提供错误名或异常名以及要\_抛出(Thrown)\_异常的对象。 你能够引发的错误或异常必须是直接或间接从属于 `Exception`(异常) 类的派生类。 案例(保存为 `exceptions_raise.py`): -
{% include "./programs/exceptions_raise.py" %}
+```python +# encoding=UTF-8 + +class ShortInputException(Exception): + '''一个由用户定义的异常类''' + def __init__(self, length, atleast): + Exception.__init__(self) + self.length = length + self.atleast = atleast + +try: + text = input('Enter something --> ') + if len(text) < 3: + raise ShortInputException(len(text), 3) + # 其他工作能在此处继续正常运行 +except EOFError: + print('Why did you do an EOF on me?') +except ShortInputException as ex: + print(('ShortInputException: The input was ' + + '{0} long, expected at least {1}') + .format(ex.length, ex.atleast)) +else: + print('No exception was raised.') + +``` 输出: -
{% include "./programs/exceptions_raise.txt" %}
+``` +$ python exceptions_raise.py +Enter something --> a +ShortInputException: The input was 1 long, expected at least 3 + +$ python exceptions_raise.py +Enter something --> abc +No exception was raised. + +``` **它是如何工作的** @@ -76,17 +132,50 @@ EOFError 在 `except` 子句中,我们提及了错误类,将该类存储 `as(为)` 相应的错误名或异常名。这类似于函数调用中的形参与实参。在这个特殊的 `except` 子句中我们使用异常对象的 `length` 与 `atleast` 字段来向用户打印一条合适的信息。 -## Try ... Finally {#try-finally} +## Try ... Finally 假设你正在你的读取中读取一份文件。你应该如何确保文件对象被正确关闭,无论是否会发生异常?这可以通过 `finally` 块来完成。 保存该程序为 `exceptions_finally.py`: -
{% include "./programs/exceptions_finally.py" %}
+```python +import sys +import time + +f = None +try: + f = open("poem.txt") + # 我们常用的文件阅读风格 + while True: + line = f.readline() + if len(line) == 0: + break + print(line, end='') + sys.stdout.flush() + print("Press ctrl+c now") + # 为了确保它能运行一段时间 + time.sleep(2) +except IOError: + print("Could not find file poem.txt") +except KeyboardInterrupt: + print("!! You cancelled the reading from the file.") +finally: + if f: + f.close() + print("(Cleaning up: Closed the file)") + +``` 输出: -
{% include "./programs/exceptions_finally.txt" %}
+``` +$ python exceptions_finally.py +Programming is fun +Press ctrl+c now +^C!! You cancelled the reading from the file. +(Cleaning up: Closed the file) + +``` **它是如何工作的** @@ -96,13 +185,18 @@ EOFError 另外要注意到我们在 `print` 之后使用了 `sys.stout.flush()`,以便它能被立即打印到屏幕上。 -## `with` 语句 {#with} +## `with` 语句 在 `try` 块中获取资源,然后在 `finally` 块中释放资源是一种常见的模式。因此,还有一个 `with` 语句使得这一过程可以以一种干净的姿态得以完成。 保存为 `exceptions_using_with.py`: -
{% include "./programs/exceptions_using_with.py" %}
+```python +with open("poem.txt") as f: + for line in f: + print(line, end='') + +``` **它是如何工作的** @@ -110,7 +204,7 @@ EOFError 在幕后发生的事情是有一项 `with` 语句所使用的协议(Protocol)。它会获取由 `open` 语句返回的对象,在本案例中就是“thefile”。 -它_总会_在代码块开始之前调用 `thefile.__enter__` 函数,并且_总会_在代码块执行完毕之后调用 `thefile.__exit__`。 +它\_总会\_在代码块开始之前调用 `thefile.__enter__` 函数,并且\_总会\_在代码块执行完毕之后调用 `thefile.__exit__`。 因此,我们在 `finally` 代码块中编写的代码应该格外留心 `__exit__` 方法的自动操作。这能够帮助我们避免重复显式使用 `try..finally` 语句。 @@ -122,12 +216,6 @@ EOFError 接下来,我们将探索 Python 的标准库。 +*** ---- - -[^1]: 在本章中 Raise 一词会经常出现,沈洁元译本大都将其译作“引发”,此处将按照具体的语境对该词的译法作出调整。 - -[^2]: 此处采用沈洁元译本的翻译。但是在其它教程或有关 Python 的讨论文章中,Handler 大都保留原文而不作翻译,这点需读者知悉。 - -[^3]: 原文作 Raising Exceptions,沈洁元译本译作“引发异常”,此处采用更流行的译法。 - +1. 此处采用沈洁元译本的翻译。但是在其它教程或有关 Python 的讨论文章中,Handler 大都保留原文而不作翻译,这点需读者知悉。 diff --git a/17.stdlib.md b/17.stdlib.md index e3aaf6d9..4a80235c 100644 --- a/17.stdlib.md +++ b/17.stdlib.md @@ -1,4 +1,4 @@ -# 标准库 {#stdlib} +# 标准库 Python 标准库(Python Standrad Library)中包含了大量有用的模块,同时也是每个标准的 Python 安装包中的一部分。熟悉 Python 标准库十分重要,因为只要你熟知这些库可以做到什么事,许多问题都能够轻易解决。 @@ -6,15 +6,14 @@ Python 标准库(Python Standrad Library)中包含了大量有用的模块 让我们来了解一些有用的模块。 -> **注意:**如果你觉得本章内容过于超前,你可以先跳过本章。不过,我强烈建议你在适应了采用 Python 进行编程后再来看看本章。 +> \*\*注意:\*\*如果你觉得本章内容过于超前,你可以先跳过本章。不过,我强烈建议你在适应了采用 Python 进行编程后再来看看本章。 -## `sys` 模块 {#sys} +## `sys` 模块 `sys` 模块包括了一些针对特定系统的功能。我们已经了解过 `sys.argv` 列表中包括了命令行参数。 想象一些我们需要检查正在使用的 Python 软件的版本,`sys` 模块会给我们相关的信息。 - ```python >>> import sys >>> sys.version_info @@ -27,23 +26,58 @@ True `sys` 模块包含一个 `version_info` 元组,它提供给我们版本信息。第一个条目是主版本信息。我们可以调出这些信息并使用它。 -## 日志模块 {#logging} +## 日志模块 如果你想将一些调试(Debugging)信息或一些重要的信息储存在某个地方,以便你可以检查你的程序是否如你所期望那般运行,应该怎么做?你应该如何将这些信息“储存在某个地方”?这可以通过 `logging` 模块来实现。 保存为 `stdlib_logging.py`: -
{% include "./programs/stdlib_logging.py" %}
+```python +import os +import platform +import logging + +if platform.platform().startswith('Windows'): + logging_file = os.path.join(os.getenv('HOMEDRIVE'), + os.getenv('HOMEPATH'), + 'test.log') +else: + logging_file = os.path.join(os.getenv('HOME'), + 'test.log') + +print("Logging to", logging_file) + +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s : %(levelname)s : %(message)s', + filename=logging_file, + filemode='w', +) + +logging.debug("Start of the program") +logging.info("Doing something") +logging.warning("Dying now") + +``` 输出: -
{% include "./programs/stdlib_logging.txt" %}
+``` +$ python stdlib_logging.py +Logging to /Users/swa/test.log + +$ cat /Users/swa/test.log +2014-03-29 09:27:36,660 : DEBUG : Start of the program +2014-03-29 09:27:36,660 : INFO : Doing something +2014-03-29 09:27:36,660 : WARNING : Dying now + +``` 如果你不能运行 `cat` 命令,你可以通过一款文本编辑器打开 `test.log` 文件。 **它是如何工作的** -我们使用了三款标准库中的模块——`os` 模块用以和操作系统交互,`platform` 模块用以获取平台——操作系统——的信息,`logging` 模块用来*记录(Log)*信息。 +我们使用了三款标准库中的模块——`os` 模块用以和操作系统交互,`platform` 模块用以获取平台——操作系统——的信息,`logging` 模块用来\*记录(Log)\*信息。 首先,我们通过检查 `platform.platform()` 返回的字符串来确认我们正在使用的操作系统(有关更多信息,请参阅`import platform; help(platform)`)。如果它是 Windows,我们将找出其主驱动器(Home Drive),主文件夹(Home Folder)以及我们希望存储信息的文件名。将这三个部分汇聚到一起,我们得到了有关文件的全部位置信息。对于其它平台而言,我们需要知道的只是用户的主文件夹位置,这样我们就可获得文件的全部位置信息。 @@ -53,10 +87,9 @@ True 最后,无论这些信息是用以调试,提醒,警告甚至是其它关键的消息,我们都可以将其聚合并记录。一旦程序开始运行,我们可以检查这一文件,从而我们便能知道程序运行过程中究竟发生了什么,哪怕在用户运行时什么信息都没有显示。 -## 每周模块系列 {#motw} +## 每周模块系列 -标准库中还有许多模块值得探索,例如一些[用以调试(Debugging)的模块](http://docs.python.org/3/library/pdb.html), -[处理命令行选项的模块](http://docs.python.org/3/library/argparse.html),[正则表达式(Regular Expressions)模块](http://docs.python.org/3/library/re.html) 等等等等。 +标准库中还有许多模块值得探索,例如一些[用以调试(Debugging)的模块](http://docs.python.org/3/library/pdb.html), [处理命令行选项的模块](http://docs.python.org/3/library/argparse.html),[正则表达式(Regular Expressions)模块](http://docs.python.org/3/library/re.html) 等等等等。 进一步探索标准库的最好方法是阅读由 Doug Hellmann 撰写的优秀的 [Python Module of the Week](http://pymotw.com/2/contents.html) 系列(你还可以阅读[它的实体书](http://amzn.com/0321767349)或是阅读 [Python 官方文档](http://docs.python.org/3/))。 @@ -64,4 +97,4 @@ True 我们已经探索了 Python 标准库中提供的诸多的模块的一些功能。在此强烈建议你浏览 [Python 标准库文档](http://docs.python.org/3/library/)来了解所有可以使用的模块。 -接下来,我们将介绍 Python 的其它各个方面,让我们的 Python 之旅更加_完整_。 +接下来,我们将介绍 Python 的其它各个方面,让我们的 Python 之旅更加\_完整\_。 diff --git a/18.more.md b/18.more.md index 712851fa..785869f3 100644 --- a/18.more.md +++ b/18.more.md @@ -38,23 +38,18 @@ 下面的表格列出了一些有用的特殊方法。如果你想了解所有的特殊方法,请[参阅手册](http://docs.python.org/3/reference/datamodel.html#special-method-names)。 -- `__init__(self, ...)` - - 这一方法在新创建的对象被返回准备使用时被调用。 - -- `__del__(self)` - - 这一方法在对象被删除之前调用(它的使用时机不可预测,所以避免使用它) - -- `__str__(self)` - - 当我们使用 `print` 函数时,或 `str()` 被使用时就会被调用。 - -- `__lt__(self, other)` - - 当_小于_运算符(<)被使用时被调用。类似地,使用其它所有运算符(+、> 等等)时都会有特殊方法被调用。 - -- `__getitem__(self, key)` - - 使用 `x[key]` 索引操作时会被调用。 - -- `__len__(self)` - - 当针对序列对象使用内置 `len()` 函数时会被调用 +* `__init__(self, ...)` + * 这一方法在新创建的对象被返回准备使用时被调用。 +* `__del__(self)` + * 这一方法在对象被删除之前调用(它的使用时机不可预测,所以避免使用它) +* `__str__(self)` + * 当我们使用 `print` 函数时,或 `str()` 被使用时就会被调用。 +* `__lt__(self, other)` + * 当\_小于\_运算符(<)被使用时被调用。类似地,使用其它所有运算符(+、> 等等)时都会有特殊方法被调用。 +* `__getitem__(self, key)` + * 使用 `x[key]` 索引操作时会被调用。 +* `__len__(self)` + * 当针对序列对象使用内置 `len()` 函数时会被调用 ## 单语句块 @@ -67,7 +62,7 @@ Yes ``` -注意,单个语句是在原地立即使用的,它不会被看作一个单独的块。尽管,你可以通过这种方式来使你的程序更加_小巧_,但除非是为了检查错误,我强烈建议你避免使用这种快捷方法,这主要是因为如果你不小心使用了一个“恰到好处”的缩进,它就很容易添加进额外的语句。 +注意,单个语句是在原地立即使用的,它不会被看作一个单独的块。尽管,你可以通过这种方式来使你的程序更加\_小巧\_,但除非是为了检查错误,我强烈建议你避免使用这种快捷方法,这主要是因为如果你不小心使用了一个“恰到好处”的缩进,它就很容易添加进额外的语句。 ## Lambda 表格 @@ -75,11 +70,21 @@ Yes 案例(保存为 `more_lambda.py`): -
{% include "./programs/more_lambda.py" %}
+```python +points = [{'x': 2, 'y': 3}, + {'x': 4, 'y': 1}] +points.sort(key=lambda i: i['y']) +print(points) + +``` 输出: -
{% include "./programs/more_lambda.txt" %}
+``` +$ python more_lambda.py +[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}] + +``` **它是如何工作的** @@ -91,11 +96,20 @@ Yes 案例(保存为 `more_list_comprehension.py`): -
{% include "./programs/more_list_comprehension.py" %}
+```python +listone = [2, 3, 4] +listtwo = [2*i for i in listone if i > 2] +print(listtwo) + +``` 输出: -
{% include "./programs/more_list_comprehension.txt" %}
+``` +$ python more_list_comprehension.py +[6, 8] + +``` **它是如何工作的** @@ -123,7 +137,7 @@ Yes 因为我们在 `args` 变量前添加了一个 `*` 前缀,函数的所有其它的额外参数都将传递到 `args` 中,并作为一个元组予以储存。如果采用的是 `**` 前缀,则额外的参数将被视为字典的键值—值配对。 -## `assert` 语句 {#assert} +## `assert` 语句 `assert` 语句用以断言(Assert)某事是真的。例如说你非常确定你正在使用的列表中至少包含一个元素,并想确认这一点,如果其不是真的,就抛出一个错误,`assert` 语句就是这种情况下的理想选择。当语句断言失败时,将会抛出 `AssertionError`。 @@ -140,35 +154,94 @@ AssertionError 你应该明智地选用 `assert` 语句。在大多数情况下,它好过捕获异常,也好过定位问题或向用户显示错误信息然后退出。 -## 装饰器 {#decorator} +## 装饰器 装饰器(Decorators)是应用包装函数的快捷方式。这有助于将某一功能与一些代码一遍又一遍地“包装”。举个例子,我为自己创建了一个 `retry` 装饰器,这样我可以将其运用到任何函数之中,如果在一次运行中抛出了任何错误,它就会尝试重新运行,直到最大次数 5 次,并且每次运行期间都会有一定的延迟。这对于你在对一台远程计算机进行网络调用的情况十分有用: -
{% include "./programs/more_decorator.py" %}
+```python +from time import sleep +from functools import wraps +import logging +logging.basicConfig() +log = logging.getLogger("retry") + + +def retry(f): + @wraps(f) + def wrapped_f(*args, **kwargs): + MAX_ATTEMPTS = 5 + for attempt in range(1, MAX_ATTEMPTS + 1): + try: + return f(*args, **kwargs) + except: + log.exception("Attempt %s/%s failed : %s", + attempt, + MAX_ATTEMPTS, + (args, kwargs)) + sleep(10 * attempt) + log.critical("All %s attempts failed : %s", + MAX_ATTEMPTS, + (args, kwargs)) + return wrapped_f + + +counter = 0 + + +@retry +def save_to_database(arg): + print("Write to a database or make a network call or etc.") + print("This will be automatically retried if exception is thrown.") + global counter + counter += 1 + # 这将在第一次调用时抛出异常 + # 在第二次运行时将正常工作(也就是重试) + if counter < 2: + raise ValueError(arg) + + +if __name__ == '__main__': + save_to_database("Some bad value") + +``` 输出: -
{% include "./programs/more_decorator.txt" %}
+``` +$ python more_decorator.py +Write to a database or make a network call or etc. +This will be automatically retried if exception is thrown. +ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {}) +Traceback (most recent call last): + File "more_decorator.py", line 14, in wrapped_f + return f(*args, **kwargs) + File "more_decorator.py", line 39, in save_to_database + raise ValueError(arg) +ValueError: Some bad value +Write to a database or make a network call or etc. +This will be automatically retried if exception is thrown. + +``` **它是如何工作的** 请参阅: -- http://www.ibm.com/developerworks/linux/library/l-cpdecor.html -- http://toumorokoshi.github.io/dry-principles-through-python-decorators.html +* http://www.ibm.com/developerworks/linux/library/l-cpdecor.html +* http://toumorokoshi.github.io/dry-principles-through-python-decorators.html -## Python 2 与 Python 3 的不同 {#two-vs-three} +## Python 2 与 Python 3 的不同 请参阅: -- ["Six" library](http://pythonhosted.org/six/) -- [Porting to Python 3 Redux by Armin](http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/) -- [Python 3 experience by PyDanny](http://pydanny.com/experiences-with-django-python3.html) -- [Official Django Guide to Porting to Python 3](https://docs.djangoproject.com/en/dev/topics/python3/) -- [Discussion on What are the advantages to python 3.x?](http://www.reddit.com/r/Python/comments/22ovb3/what_are_the_advantages_to_python_3x/) +* ["Six" library](http://pythonhosted.org/six/) +* [Porting to Python 3 Redux by Armin](http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/) +* [Python 3 experience by PyDanny](http://pydanny.com/experiences-with-django-python3.html) +* [Official Django Guide to Porting to Python 3](https://docs.djangoproject.com/en/dev/topics/python3/) +* [Discussion on What are the advantages to python 3.x?](http://www.reddit.com/r/Python/comments/22ovb3/what\_are\_the\_advantages\_to\_python\_3x/) ## 总结 我们在本章中介绍了有关 Python 的更多功能,不过我们还未涵盖到 Python 的所有功能。不过,在这一阶段,我们已经涉猎了大多数你将在实践中遇到的内容。这足以让你开始编写任何你所期望的程序。 -接下来,我们将讨论如何进一步探索 Python。 \ No newline at end of file +接下来,我们将讨论如何进一步探索 Python。 diff --git a/19.what_next.md b/19.what_next.md index 7ccf659b..ffefe92f 100644 --- a/19.what_next.md +++ b/19.what_next.md @@ -1,14 +1,14 @@ -# 迈出下一步[^1] +# 迈出下一步 如果到现在你已经阅读过本书并且编写了许多程序,那么你一定已经开始熟悉并且习惯 Python 了。或许你已经创建了一些 Python 程序来尝试完成一些工作,同时锻炼你自己的 Python 技能。如果你尚未至此,你也应该作出努力。现在我们面临的问题是“下一步该做什么?”。 我会建议你试图解决这个问题: -> 编写一款你自己的命令行*地址簿*程序,你可以用它浏览、添加、编辑、删除或搜索你的联系人,例如你的朋友、家人、同事,还有他们诸如邮件地址、电话号码等多种信息。这些详细信息必须被妥善储存以备稍后的检索。 +> 编写一款你自己的命令行_地址簿_程序,你可以用它浏览、添加、编辑、删除或搜索你的联系人,例如你的朋友、家人、同事,还有他们诸如邮件地址、电话号码等多种信息。这些详细信息必须被妥善储存以备稍后的检索。 -如果你回想至今我们学过、讨论过、遇见过的所有东西,你会发现这其实非常简单。如果你仍想要有关如何进行的提示,这儿倒是有一些。[^2] +如果你回想至今我们学过、讨论过、遇见过的所有东西,你会发现这其实非常简单。如果你仍想要有关如何进行的提示,这儿倒是有一些。 -一旦你能够做到这件事,你便可以说自己是一名 Python 程序员了。现在,赶快[写封邮件]({{ book.contactUrl }})来感谢我写出了这么棒的一本书 ;-)。这一步并非强制但我仍建议如此。同时,请考虑[购买本书的实体书]({{ book.buyBookUrl }})来支持本书的后续改进。 +一旦你能够做到这件事,你便可以说自己是一名 Python 程序员了。现在,赶快\[写封邮件]\(\{{ book.contactUrl \}})来感谢我写出了这么棒的一本书 ;-)。这一步并非强制但我仍建议如此。同时,请考虑\[购买本书的实体书]\(\{{ book.buyBookUrl \}})来支持本书的后续改进。 如果你觉得上面的程序太容易了,这还有另一个: @@ -22,47 +22,47 @@ 你还可以看看: -- [Exercises for Programmers: 57 Challenges to Develop Your Coding Skills](https://pragprog.com/book/bhwb/exercises-for-programmers) -- [Intermediate Python Projects](https://openhatch.org/wiki/Intermediate_Python_Workshop/Projects) +* [Exercises for Programmers: 57 Challenges to Develop Your Coding Skills](https://pragprog.com/book/bhwb/exercises-for-programmers) +* [Intermediate Python Projects](https://openhatch.org/wiki/Intermediate\_Python\_Workshop/Projects) ## 示例代码 学习一门编程语言的最好方式就是编写大量代码,并阅读大量代码: -- [Python Cookbook](http://code.activestate.com/recipes/langs/python/) 是一本极具价值的“烹饪法”与提示的集合,它介绍了如何通过 Python 解决某些特定类型的问题。 -- [Python Module of the Week](http://pymotw.com/2/contents.html) 是另一本优秀的[标准库](./stdlib.md#stdlib)必读指南。 +* [Python Cookbook](http://code.activestate.com/recipes/langs/python/) 是一本极具价值的“烹饪法”与提示的集合,它介绍了如何通过 Python 解决某些特定类型的问题。 +* [Python Module of the Week](http://pymotw.com/2/contents.html) 是另一本优秀的[标准库](stdlib.md#stdlib)必读指南。 ## 建议 -- [The Hitchhiker's Guide to Python!](http://docs.python-guide.org/en/latest/) -- [The Elements of Python Style](https://github.com/amontalenti/elements-of-python-style) -- [Python Big Picture](http://slott-softwarearchitect.blogspot.ca/2013/06/python-big-picture-whats-roadmap.html) -- ["Writing Idiomatic Python" ebook](http://www.jeffknupp.com/writing-idiomatic-python-ebook/) (付费) +* [The Hitchhiker's Guide to Python!](http://docs.python-guide.org/en/latest/) +* [The Elements of Python Style](https://github.com/amontalenti/elements-of-python-style) +* [Python Big Picture](http://slott-softwarearchitect.blogspot.ca/2013/06/python-big-picture-whats-roadmap.html) +* ["Writing Idiomatic Python" ebook](http://www.jeffknupp.com/writing-idiomatic-python-ebook/) (付费) ## 视频 -- [Full Stack Web Development with Flask](https://github.com/realpython/discover-flask) -- [PyVideo](http://www.pyvideo.org) +* [Full Stack Web Development with Flask](https://github.com/realpython/discover-flask) +* [PyVideo](http://www.pyvideo.org) ## 问与答 -- [Official Python Dos and Don'ts](http://docs.python.org/3/howto/doanddont.html) -- [Official Python FAQ](http://www.python.org/doc/faq/general/) -- [Norvig's list of Infrequently Asked Questions](http://norvig.com/python-iaq.html) -- [Python Interview Q & A](http://dev.fyicenter.com/Interview-Questions/Python/index.html) -- [StackOverflow questions tagged with python](http://stackoverflow.com/questions/tagged/python) +* [Official Python Dos and Don'ts](http://docs.python.org/3/howto/doanddont.html) +* [Official Python FAQ](http://www.python.org/doc/faq/general/) +* [Norvig's list of Infrequently Asked Questions](http://norvig.com/python-iaq.html) +* [Python Interview Q & A](http://dev.fyicenter.com/Interview-Questions/Python/index.html) +* [StackOverflow questions tagged with python](http://stackoverflow.com/questions/tagged/python) ## 教程 -- [Hidden features of Python](http://stackoverflow.com/q/101268/4869) -- [What's the one code snippet/python trick/etc did you wish you knew when you learned python?](http://www.reddit.com/r/Python/comments/19dir2/whats_the_one_code_snippetpython_tricketc_did_you/) -- [Awaretek's comprehensive list of Python tutorials](http://www.awaretek.com/tutorials.html) +* [Hidden features of Python](http://stackoverflow.com/q/101268/4869) +* [What's the one code snippet/python trick/etc did you wish you knew when you learned python?](http://www.reddit.com/r/Python/comments/19dir2/whats\_the\_one\_code\_snippetpython\_tricketc\_did\_you/) +* [Awaretek's comprehensive list of Python tutorials](http://www.awaretek.com/tutorials.html) ## 讨论 如果你遇到了一个 Python 问题,但不知道该问谁,那么 [python-tutor list](http://mail.python.org/mailman/listinfo/tutor) 是你提问的最佳场所。 -请确保你会自己做你的家庭作业,你会首先尝试自己解决问题,同时,还要会[问聪明的问题](http://catb.org/~esr/faqs/smart-questions.html)。 +请确保你会自己做你的家庭作业,你会首先尝试自己解决问题,同时,还要会[问聪明的问题](http://catb.org/\~esr/faqs/smart-questions.html)。 ## 新闻 @@ -78,9 +78,9 @@ 学习使用 [Flask](http://flask.pocoo.org) 来创建你自己的网站。下面这些资源有助于你开始学习: -- [Flask Official Quickstart](http://flask.pocoo.org/docs/quickstart/) -- [The Flask Mega-Tutorial](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) -- [Example Flask Projects](https://github.com/mitsuhiko/flask/tree/master/examples) +* [Flask Official Quickstart](http://flask.pocoo.org/docs/quickstart/) +* [The Flask Mega-Tutorial](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) +* [Example Flask Projects](https://github.com/mitsuhiko/flask/tree/master/examples) ## 图形软件 @@ -88,17 +88,13 @@ 使用 Python 的 GUI 有许多选择: -- Kivy - - http://kivy.org - -- PyGTK - - 这是 GTK+ 工具包的 Python 绑定,它是构建 GNOME 的基础。GTK+ 有许多奇怪的用法,但是你一旦习惯了使用它,就能很快的创建出你的 GUI 应用。Glade 图形界面设计工具是不可或缺的。它的文档至今仍在不断改进。GTK+ 在 GNU/Linux 下能够良好工作,但是它针对 Windows 平台的移植工作尚未完成。你可以使用 GTK+ 创建免费或专有的软件。要想开始使用,请阅读 [PyGTK 教程](http://www.pygtk.org/tutorial.html)。 - -- PyQt -    - 这是 Qt 工具包的 Python 绑定,它是构建 KDE 的基础。 受益于 Qt Designer 与令人惊讶的 Qt 文档,Qt 十分容易使用也十分强大。如果你希望创建一款开源(GPL)软件,你可以免费使用 PyQt,不过如果你想创建专有的闭源软件,你需要购买它。从 Qt 4.5 开始你可以使用它来创建不采用 GPL 授权的软件。要想开始使用,请阅读 [PySide](http://qt-project.org/wiki/PySide)。 - -- wxPython - - 这是 wxWidgets 工具包的 Python 绑定。wxPython 有一个与之相关的学习曲线。不过,它非常便携,并且可以运行在 GNU/Linux、Windwos、Mac、甚至是嵌入式平台中。有许多 IDE 可以采用 wxPython,并且包含了 GUI 设计工具,例如 [SPE (Stani's Python Editor)](http://spe.pycs.net/) 还有 [wxGlade](http://wxglade.sourceforge.net/) GUI 构建工具。你可以使用 wxPython 来创建免费或专有的软件。要想开始使用,请阅读[wxPython 教程](http://zetcode.com/wxpython/)。 +* Kivy + * http://kivy.org +* PyGTK + * 这是 GTK+ 工具包的 Python 绑定,它是构建 GNOME 的基础。GTK+ 有许多奇怪的用法,但是你一旦习惯了使用它,就能很快的创建出你的 GUI 应用。Glade 图形界面设计工具是不可或缺的。它的文档至今仍在不断改进。GTK+ 在 GNU/Linux 下能够良好工作,但是它针对 Windows 平台的移植工作尚未完成。你可以使用 GTK+ 创建免费或专有的软件。要想开始使用,请阅读 [PyGTK 教程](http://www.pygtk.org/tutorial.html)。 +* PyQt - 这是 Qt 工具包的 Python 绑定,它是构建 KDE 的基础。 受益于 Qt Designer 与令人惊讶的 Qt 文档,Qt 十分容易使用也十分强大。如果你希望创建一款开源(GPL)软件,你可以免费使用 PyQt,不过如果你想创建专有的闭源软件,你需要购买它。从 Qt 4.5 开始你可以使用它来创建不采用 GPL 授权的软件。要想开始使用,请阅读 [PySide](http://qt-project.org/wiki/PySide)。 +* wxPython + * 这是 wxWidgets 工具包的 Python 绑定。wxPython 有一个与之相关的学习曲线。不过,它非常便携,并且可以运行在 GNU/Linux、Windwos、Mac、甚至是嵌入式平台中。有许多 IDE 可以采用 wxPython,并且包含了 GUI 设计工具,例如 [SPE (Stani's Python Editor)](http://spe.pycs.net/) 还有 [wxGlade](http://wxglade.sourceforge.net/) GUI 构建工具。你可以使用 wxPython 来创建免费或专有的软件。要想开始使用,请阅读[wxPython 教程](http://zetcode.com/wxpython/)。 ### GUI 工具总结 @@ -110,20 +106,18 @@ ## 各种实现 -编程语言主要有两部分——语言与软件。语言是你_如何_编写,软件是你_怎样_实际运行我们的程序。 +编程语言主要有两部分——语言与软件。语言是你\_如何\_编写,软件是你\_怎样\_实际运行我们的程序。 -我们一直在使用 _CPython_ 软件来运行我们的程序。它被成为 CPython 是因为它是使用 C 语言编写的,同时它也是_经典的(Classical) Python 解释器_。 +我们一直在使用 _CPython_ 软件来运行我们的程序。它被成为 CPython 是因为它是使用 C 语言编写的,同时它也是\_经典的(Classical) Python 解释器\_。 还有其他软件可以运行你的 Python 程序: -- [Jython](http://www.jython.org) - - 在 Java 平台上运行的 Python 实现。这意味着你可以在 Python 语言中使用 Java 的库与类,反之亦然。 - -- [IronPython](http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython) - - 在 .NET 平台上运行的 Python 实现。这意味着你可以在 Python 语言中使用 .NET 的库与类,反之亦然 - -- [PyPy](http://codespeak.net/pypy/dist/pypy/doc/home.html) - - 用 Python 编写的 Python 实现!这是一项研究项目,旨在于使其能快速且方便的改进解释器,因为解释器本身就是用动态语言编写的了(而不是采用上述三种 C、Java、C# 等动态语言来编写)。 +* [Jython](http://www.jython.org) + * 在 Java 平台上运行的 Python 实现。这意味着你可以在 Python 语言中使用 Java 的库与类,反之亦然。 +* [IronPython](http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython) + * 在 .NET 平台上运行的 Python 实现。这意味着你可以在 Python 语言中使用 .NET 的库与类,反之亦然 +* [PyPy](http://codespeak.net/pypy/dist/pypy/doc/home.html) + * 用 Python 编写的 Python 实现!这是一项研究项目,旨在于使其能快速且方便的改进解释器,因为解释器本身就是用动态语言编写的了(而不是采用上述三种 C、Java、C# 等动态语言来编写)。 还有其它诸如 [CLPython](http://common-lisp.net/project/clpython/)——采用 Common Lisp 编写的 Python 实现,和[Brython](http://brython.info/) ,它在 JavaScript 解释器之上实现,意味着你可以使用 Python(而非 JavaScript)编写你的 Web 浏览器(“Ajax”)程序。 @@ -131,21 +125,18 @@ ## 函数式编程(面向高阶读者){#functional-programming} -当你开始编写更加庞大的程序时,你应该清楚了解更多关于使用函数的方式来进行编程,而不是我们在[《面向对象编程》章节中](./14.oop.md#oop)所学习的基于类的方式进行编程: +当你开始编写更加庞大的程序时,你应该清楚了解更多关于使用函数的方式来进行编程,而不是我们在[《面向对象编程》章节中](14.oop.md#oop)所学习的基于类的方式进行编程: -- [Functional Programming Howto by A.M. Kuchling](http://docs.python.org/3/howto/functional.html) -- [Functional programming chapter in 'Dive Into Python' book](http://www.diveintopython.net/functional_programming/index.html) -- [Functional Programming with Python presentation](http://ua.pycon.org/static/talks/kachayev/index.html) -- [Funcy library](https://github.com/Suor/funcy) -- [PyToolz library](http://toolz.readthedocs.org/en/latest/) +* [Functional Programming Howto by A.M. Kuchling](http://docs.python.org/3/howto/functional.html) +* [Functional programming chapter in 'Dive Into Python' book](http://www.diveintopython.net/functional\_programming/index.html) +* [Functional Programming with Python presentation](http://ua.pycon.org/static/talks/kachayev/index.html) +* [Funcy library](https://github.com/Suor/funcy) +* [PyToolz library](http://toolz.readthedocs.org/en/latest/) ## 总结 现在我们已经行至本书末尾,不过,正如人们所说,这是_昭示着开始的终结_!你现在已经是一名狂热 Python 用户,毫无疑问,你已准备好通过 Python 来解决诸多问题了。你可以开始自动化你的电脑,去做任何你以前难以想象的事情,你可以开始编写你自己的游戏,开始做更多更多的事,远不仅此。来,让我们出发吧! - ---- - -[^1]: 本章所提供的所有学习资源均为英文,故对于其标题不作翻译。 +*** -[^2]: 创建一个类用来表示人的信息。使用一份字典来存储人物对象,将它们的名字当作键值。使用 pickle 模块来将对象长久地存储在硬盘上。使用字典的内置方法来添加、删除或编辑地址簿中的人物。——原书注 +1. 创建一个类用来表示人的信息。使用一份字典来存储人物对象,将它们的名字当作键值。使用 pickle 模块来将对象长久地存储在硬盘上。使用字典的内置方法来添加、删除或编辑地址簿中的人物。——原书注 diff --git a/README.md b/README.md index b2ecbc8b..bcf65ff7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 简明 Python 教程 +# 介绍 《A Byte of Python》是一本由 **Swaroop C H** 编写,旨在于介绍如何使用 Python 语言进行编程的自由图书。它以教材与指南的形式为入门者介绍 Python 语言。如果你对电脑知识的了解仅限于如何保存文本文件的话,那这本书就是为你准备的。 @@ -10,243 +10,107 @@ 本书将指导你如何使用 Python 3。同时,本书也会以指南的形式告诉你应如何适应虽老些但使用更加普遍的 Python 2。 -## 都有谁阅读了本书? {#who-reads-bop} +## 都有谁阅读了本书? 下面是一些人对本书的评价: -> 这本书是我所见过的最好的新手教程!感谢你所做的努力。 -> ——[Walt Michalik](mailto:wmich50@theramp.net) +> 这本书是我所见过的最好的新手教程!感谢你所做的努力。 ——[Walt Michalik](mailto:wmich50@theramp.net) - +> 我所遇见的最好的事就是发现了《简明 Python 教程》,它确实是一本为新手准备的绝佳书籍。它撰写出色,清楚解释了诸多定义,并都配以了清晰的案例。 -- [Joshua Robin](mailto:joshrob@poczta.onet.pl) -> 我所遇见的最好的事就是发现了《简明 Python 教程》,它确实是一本为新手准备的绝佳书籍。它撰写出色,清楚解释了诸多定义,并都配以了清晰的案例。 -> -- [Joshua Robin](mailto:joshrob@poczta.onet.pl) +> 一份面向新手的出色而温和的 #Python 编程指南。 -- [Shan Rajasekaran](https://twitter.com/ShanRajasekaran/status/268910645842423809) - +> 最佳 Python 新手指南 -- [Nickson Kaigi](https://twitter.com/nickaigi/status/175508815729541120) -> 一份面向新手的出色而温和的 #Python 编程指南。 -> -- [Shan Rajasekaran](https://twitter.com/ShanRajasekaran/status/268910645842423809) +> 在每一页的阅读中开始爱上 Python。 -- [Herbert Feutl](https://twitter.com/HerbertFeutl/status/11901471389913088) - +> Python 的最佳新手教程,它将会给你一把打开 Python 奇幻世界的钥匙。 -- [Dilip](https://twitter.com/Dili\_mathilakam/status/220033783066411008) -> 最佳 Python 新手指南 -> -- [Nickson Kaigi](https://twitter.com/nickaigi/status/175508815729541120) +> 我本应开始我实际的“工作”,但我却恰巧发现了这本《简明 Python 教程》。一本附有优秀例子的杰出教材。 -- [Biologist John](https://twitter.com/BiologistJohn/statuses/194726001803132928) - +> 最近我开始阅读《简明 Python 教程》。一部优秀的作品。而且它是免费的。强烈推荐给那些具有抱负的 Pythonistas(译注:热衷使用 Python 语言编程的人)。 -- [Mangesh](https://twitter.com/mangeshnanoti/status/225680668867321857) -> 在每一页的阅读中开始爱上 Python。 -> -- [Herbert Feutl](https://twitter.com/HerbertFeutl/status/11901471389913088) +> 《简明 Python 教程》,由 Swaroop 撰写(我目前在读的书)。可能是最适合用以入门,也可能世界上最好的面向每一位新手甚至是有经验的用户的教材。 -- [Apostolos](http://apas.gr/2010/04/27/learning-python/) - +> 阅读 @swaroopch 所撰写的最好的一本书 #ByteOfPython 确是享受。 -- [Yuvraj Sharma](https://twitter.com/YuvrajPoudyal/status/448050415356346368) -> Python 的最佳新手教程,它将会给你一把打开 Python 奇幻世界的钥匙。 -> -- [Dilip](https://twitter.com/Dili_mathilakam/status/220033783066411008) +> 十分感谢你撰写了《简明 Python 教程》。我两天前才刚刚开始学习如何编写代码,现在我已经完成了两款简单游戏的编写。你编写的教程十分完美,我在此就是想让你知道它是多么富有价值。 -- Franklin - +> 我是一名来自 Dayanandasagar 大学的工程学(第七期,CSE)学生。首先,我想说你的《简明 Python 教程》这本书非常适合像我这样的 Python 新手。被出色解释清楚的概念以及简单的例子帮助我更加容易地学习 Python。十分感谢你。 -- Madhura -> 我本应开始我实际的“工作”,但我却恰巧发现了这本《简明 Python 教程》。一本附有优秀例子的杰出教材。 -> -- [Biologist John](https://twitter.com/BiologistJohn/statuses/194726001803132928) +> 我是一名 18 岁学生,现在在爱尔兰的大学学习信息技术。我希望能在此表达我的感激之情:感谢你写出了《简明 Python 教程》。此前我已经具备了 3 门编程语言的知识——C,Java,还有 Javascript。而 Python 是我所接触并学习过的编程语言里最简单的一门,这全都要归功于你的这本书,它是如此优秀,将学习 Python 的历程变得如此简单而有趣。这是我所读过的有关编程的书籍里最优秀的一本。祝贺你所完成的这项伟大工作,并希望你能将它继续下去。 -- Matt - +> 嗨,我来自多米尼加共和国,我的名字是 Pavel。最近我读了你的 _《简明 Python 教程》_,发现它是如此精彩!:)我从这些范例中学到了很多。你的这本书对像我这般的新手提供了很大的帮助。 -- [Pavel Simo](mailto:pavel.simo@gmail.com) -> 最近我开始阅读《简明 Python 教程》。一部优秀的作品。而且它是免费的。强烈推荐给那些具有抱负的 Pythonistas(译注:热衷使用 Python 语言编程的人)。 -> -- [Mangesh](https://twitter.com/mangeshnanoti/status/225680668867321857) +> 我是一名来自中国的学生。现在,我读了你的《简明 Python 教程》这本书,不由感叹它实在是太美妙了。这本书是如此简明扼要但却能帮助所有第一次学习编程的人。你知道,我对 Java 抱有兴趣,并且运行过很多次云计算。我曾为服务器编写程序,所以我觉得 Python 会是一个好选择。在阅读完你的这本书后,我觉得这不仅仅只是一个好选择,而是我必须、理应使用 Python。我的英语算不上很好,寄这一封邮件只是想向你诉说一声“谢谢”!为你与你的家人致以我最好的祝福。 -- Roy Lau - +> 我最近刚刚完成了对《简明 Python 教程》的阅读,我觉得我实在应当感谢你。在阅读到最后一页后,我对自己将要重归于其它沉闷、枯燥、乏味的 Python 教程与指南中而伤心不已。无论如何,我真的很感谢你的这本书。 -- [Samuel Young](mailto:sy137@gmail.com) -> 《简明 Python 教程》,由 Swaroop 撰写(我目前在读的书)。可能是最适合用以入门,也可能世界上最好的面向每一位新手甚至是有经验的用户的教材。 -> -- [Apostolos](http://apas.gr/2010/04/27/learning-python/) +> 亲爱的 Swaroop,我正上着一门对教学了无兴趣的教师所教授的课程。我们正在使用由 O'Reilly 出品的《Python 学习手册(Learning Python)》第二版。它并非面向没有任何编程知识的初学者的教材,而一名教师应该在另一种领域来进行教学。非常感谢你的这本书,如果没有它那我在 Python 和编程面前只能碌碌无为。真的是万分感谢,你能够\_打破这信息的壁垒\_使得每一个初学者都可以理解这些内容,这并非每个人都能做到的事情。 -- [Joseph Duarte](mailto:jduarte1@cfl.rr.com) - +> 我真是喜欢你的这本书!它真的是最好最好的 Python 教程,同时也是非常有用的参考。令人赞叹,真正的杰作!愿你能够继续这项伟大的工作! -- Chris-André Sommerseth -> 阅读 @swaroopch 所撰写的最好的一本书 #ByteOfPython 确是享受。 -> -- [Yuvraj Sharma](https://twitter.com/YuvrajPoudyal/status/448050415356346368) +> 首先,我希望能够向你表达我对这本优秀的书的感谢。我认为这是一本对于那些正在寻找优秀的 Python 初学者教程的人的最佳教材。 我想可能是在两三年前,当我第一次听说这本书时,那时的我尚不能阅读英语撰写的书籍,所以我找到了一本中文译本,是那本中文译本将我带进了 Python 编程世界的大门。 最近,我重新读了这本书。当然,这一次我读的是英语版本的。我简直不敢相信我可以不借助手边的字典就读完这本书。自然,它全应归功于你的工作,是你让这本书变得如此易于理解。 -- [myd7349](https://github.com/swaroopch/byte\_of\_python/pull/13) - +> 我在此通过邮件对你在网络上撰写的《简明 Python 教程》向你表达感谢。在遇到你的这本书之前,我曾花费数月的时间来尝试使用 Python,尽管我通过 pyGame 获得了些许收获,但我还尚未完成一款程序。 感谢你简化了个中类别,使得学习 Python 真的变成了看起来能够达到的目标。现在看来我已经学会了 Python 的基础,并且能够继续下去,实现我的目标——游戏开发。 …… 再一次感谢你在网络上提供这本结构化、对基础编程很有帮助的教程。它助我对 OOP(面向对象编程)内外都有了足够的理解,这是过去我所学习的两本教材都没能做到的事情。 -- [Matt Gallivan](mailto:m\_gallivan12@hotmail.com) -> 十分感谢你撰写了《简明 Python 教程》。我两天前才刚刚开始学习如何编写代码,现在我已经完成了两款简单游戏的编写。你编写的教程十分完美,我在此就是想让你知道它是多么富有价值。 -> -- Franklin +> 我要感谢你和你的书 _《简明 Python 教程》_,它是我所能找到的最好的编程学习方式。我的名字叫 Ahmed,15岁,来自埃及。Python 是我学习的第二门编程语言。我曾在学校学习了 Visual Basic 6,但并不是很喜欢它,但现在我十分享受学习 Python 的过程。我编写了一款通讯录程序并且取得了成功。我将开始尝试编写更多程序,也试着去阅读 Python 程序(如果你能告诉我它们的源代码,那会对我大有帮助)。我现在也开始学习 Java,如果你能够告诉我哪里能找到如你的这本书这般优秀的 Java 教程,那真的是帮到我大忙了。感谢你。 -- [Ahmed Mohammed](mailto:sedo\_91@hotmail.com) - +> 由 Swaroop C H 撰写的《简明 Python 教程》这本 110 页的 PDF 教程是针对想要更多地了解 Python 的初学者的绝佳资源。它精心编写,易于跟随,同时还可能是针对 Python 编程的最佳介绍。 -- [Drew Ames](http://www.linux.com/feature/126522) -> 我是一名来自 Dayanandasagar 大学的工程学(第七期,CSE)学生。首先,我想说你的《简明 Python 教程》这本书非常适合像我这样的 Python 新手。被出色解释清楚的概念以及简单的例子帮助我更加容易地学习 Python。十分感谢你。 -> -- Madhura +> 昨天我在我的诺基亚 N800 上阅读了《简明 Python 教程》的大部分内容,这是我所遇到过的最简单也最简洁的 Python 介绍。强烈推荐以这本书作为你学习 Python 的起点。 -- [Jason Delport](http://paxmodept.com/telesto/blogitem.htm?id=627) - +> @swaroopch 撰写的《简明 Vim 教程(Byte of Vim)》与《简明 Python 教程》是我到目前所遇见的最好的技术写作作品。它们都是优秀的作品。#FeelGoodFactor -- [Surendran](http://twitter.com/suren/status/12840485454) -> 我是一名 18 岁学生,现在在爱尔兰的大学学习信息技术。我希望能在此表达我的感激之情:感谢你写出了《简明 Python 教程》。此前我已经具备了 3 门编程语言的知识——C,Java,还有 Javascript。而 Python 是我所接触并学习过的编程语言里最简单的一门,这全都要归功于你的这本书,它是如此优秀,将学习 Python 的历程变得如此简单而有趣。这是我所读过的有关编程的书籍里最优秀的一本。祝贺你所完成的这项伟大工作,并希望你能将它继续下去。 -> -- Matt +> 《简明 Python 教程》是最好的。 (对问题“有人能推荐一本优秀且便宜的用来学习 Python 基础的资源吗?”的回答) -- [Justin LoveTrue](http://www.facebook.com/pythonlang/posts/406873916788) - +> 《简明 Python》十分有帮助……万分感谢。:) [Chinmay](https://twitter.com/a\_chinmay/status/258822633741762560) -> 嗨,我来自多米尼加共和国,我的名字是 Pavel。最近我读了你的 _《简明 Python 教程》_,发现它是如此精彩!:)我从这些范例中学到了很多。你的这本书对像我这般的新手提供了很大的帮助。 -> -- [Pavel Simo](mailto:pavel.simo@gmail.com) +> 一直以来都是《简明 Python 教程》的粉丝——它同时为新程序员与有经验的程序员所编写。 -- [Patrick Harrington](http://stackoverflow.com/a/457785/4869) - +> 从几天前我开始从你的书中学习 Python……感谢这本优秀的书。它撰写的如此优秀,使我的学习生活 更加容易……现在你有了一名新粉丝——那就是我。:)万分感谢。 -- [Gadadhari Bheem](https://twitter.com/Pagal\_e\_azam/statuses/242865885256232960) -> 我是一名来自中国的学生。现在,我读了你的《简明 Python 教程》这本书,不由感叹它实在是太美妙了。这本书是如此简明扼要但却能帮助所有第一次学习编程的人。你知道,我对 Java 抱有兴趣,并且运行过很多次云计算。我曾为服务器编写程序,所以我觉得 Python 会是一个好选择。在阅读完你的这本书后,我觉得这不仅仅只是一个好选择,而是我必须、理应使用 Python。我的英语算不上很好,寄这一封邮件只是想向你诉说一声“谢谢”!为你与你的家人致以我最好的祝福。 -> -- Roy Lau +> 在我学习 Python 之前,我已经具有了 Assembly、C、C++、C# 和 Java 的基本编程能力。我想学习 Python 的原因是它十分流行(人们都在谈论它)且功能强大(现实如此)。这本由 Swaroop 先生所撰写的书是一本非常好的教材,它同时面向新程序员与新 Python 程序员。我花了 10 个半天来读完它,十分有帮助! -- [Fang Biyi (电气与计算机工程学博士候选人,密歇根州立大学)](mailto:fangbiyi@gmail.com) - +> 为这本书向你致谢!! 这本书消除了我在 Python 方面诸如面向对象编程等许多问题。 我不觉得我是 OO 方面的专家,但我知道这本书在我迈出的第一与第二步上帮助颇多。 我已经编写了几款 Python 程序,它们确实在我的系统管理员工作中帮我解决了诸多事情。它们都是程序性的,但是在许多人的标准看来它们都如此小巧。 再次感谢这本书。感谢你将它公开在网络上。 -- Bob -> 我最近刚刚完成了对《简明 Python 教程》的阅读,我觉得我实在应当感谢你。在阅读到最后一页后,我对自己将要重归于其它沉闷、枯燥、乏味的 Python 教程与指南中而伤心不已。无论如何,我真的很感谢你的这本书。 -> -- [Samuel Young](mailto:sy137@gmail.com) +> 我希望为你撰写的这本我所阅读过的最佳编程书籍向你表示感谢。Python 并不是我使用的第一门语言,但我可以想象它可以拥有的一切可能性。十分感谢你予我这个工具,让我可以创造那些我从未想过我能创造的一切。 -- "The Walrus" - +> 我希望为你所撰写的 _《简明 Python 教程》(第 2 版与第 3 版)_ 向你表示感谢。它在我整个学习 Python 与编程的历程中弥足珍贵。 不必多言,我是编程世界里的一名新手,我耗费了几个月的时间自己学习从而达到这样的程度。我曾通过 Yotube 教程和其它的一些诸如免费图书的在线教程来学习编程。就在昨天我决定深入学习你的这本书,现在我已经学了开头的几页,这比我在其他任何一本书或教程中所走过的进度都要多。有些事我曾一度感到困惑,但在这本书里这些**优秀**的解释和范例面前都得以解答。我已经等不及去阅读(学习)之后的更多内容了! 非常感谢你,不仅是撰写了这本书,还愿意把它通过知识共享协议授权分发(免费地)。感谢上帝,正是如此这样无私的人们帮助并教导了我们其余的这些人。 -- Chris -> 亲爱的 Swaroop,我正上着一门对教学了无兴趣的教师所教授的课程。我们正在使用由 O'Reilly 出品的《Python 学习手册(Learning Python)》第二版。它并非面向没有任何编程知识的初学者的教材,而一名教师应该在另一种领域来进行教学。非常感谢你的这本书,如果没有它那我在 Python 和编程面前只能碌碌无为。真的是万分感谢,你能够_打破这信息的壁垒_使得每一个初学者都可以理解这些内容,这并非每个人都能做到的事情。 -> -- [Joseph Duarte](mailto:jduarte1@cfl.rr.com) +> 在 2011 年时我曾向你写信,那时我才刚刚开始使用 Python,并想为你的教程《简明 Python 教程》向你表示感谢。如果没有它,我想我只会倒在路边。自那时起我已经在我的组织中使用 Python 这门语言编写程序,实现诸多功能,而在未来我相信我能写得更多。无论如何我也不会把自己称作一名高级程序员,但我发现自从我开始使用 Python 后,现在我时不时会收到来自他人的协助请求。我发现,在阅读《简》时,我已经放弃学习 C 和 C++,因为那些书里在一开始就向我抛出了一个包含增量赋值(Augmented Assignment)的例子。自然,没有任何为何有关在此安排这一赋值的解释,我只能用尽我的头脑去弄清楚纸上印出来的到底是什么内容。这是我所能记得的最令人沮丧的经历,最终我选择了放弃。这并不是意味着 C 或 C++ 是不可能学会的,抑或我是一个蠢蛋,但它的确意味着我工作中所拥有的文档不会包括任何有关符号或词语的定义,而这些确是在任何介绍中都至关重要的部分。正如计算机不能理解在其所使用的语言的语法之外的计算机词汇或计算机符号一般 ,任何一个领域的新学生如果遇到的全都是没有定义的符号与词汇,他就不能领会其主题所在。你会遇到某一情况下的“蓝屏”。其解决方案简单明了:找到个中词汇与符号并理解其正确的定义,如此一来——你瞧,计算机和学生都可以继续进行他们的任务。你的这本书将二者结合得如此之好,我在其中只有很少的部分无法掌握。因此,谢谢你。我鼓励您继续在书中囊括各术语的完整定义。一旦你有所了解,就能知道Python 的文档是优秀的(于我所见,范例就是它的力量)。但是在许多情况下,为了理解文档,你必须了解在我看来并不需要知道的东西。第三方教程都表示需要借助文档来澄清,它们的成功很大程度上都归功于那些用来描述术语的词语。我已经将你的这本书推荐给其他许多人。有的来自澳大利亚,有的在加勒比,还有一些在美国。它填补了其他人没能填补的位置。我希望你能继续这样好好做下去,并祝愿你在未来的所有成功。 -- Nick - +> 嗨,我是 Ankush(19岁)。我现在正在面对开始学习 Python 的巨大困难。我尝试了许多教材但它们都过于臃肿,而且也非目标导向;尔后我便遇到这可爱的一本,让我在短时间内就爱上了 Python。十分感谢这本“美妙的一本书”。 -- Ankush -> 我真是喜欢你的这本书!它真的是最好最好的 Python 教程,同时也是非常有用的参考。令人赞叹,真正的杰作!愿你能够继续这项伟大的工作! -> -- Chris-André Sommerseth +> 我要感谢你这本出色的 Python 指南。我是一位分子生物学家(仅有些许编程背景),在我的工作中我需要处理有关 DNA 序列的大数据集,还要分析显微镜图像。对于这些工作,采用 Python 编程对我十分有帮助,如果不是我必须要完成并发表一项历时六年的项目的话。 这样一本教程能够免费提供是邪恶尚未统治世界的确切标志!:) -- Luca - +> 既然这(Python)将是你学习的第一门编程语言,你应该采用《简明 Python 教程》。它确实为 Python 编程提供了恰当的介绍,且节奏十分适合一般初学者。在此之后最重要的事自然是切实地开始开始练习编写你自己的小程序。 -- ["{Unregistered}"](http://www.overclock.net/t/1177951/want-to-learn-programming-where-do-i-start#post\_15837176) -> 首先,我希望能够向你表达我对这本优秀的书的感谢。我认为这是一本对于那些正在寻找优秀的 Python 初学者教程的人的最佳教材。 -> 我想可能是在两三年前,当我第一次听说这本书时,那时的我尚不能阅读英语撰写的书籍,所以我找到了一本中文译本,是那本中文译本将我带进了 Python 编程世界的大门。 -> 最近,我重新读了这本书。当然,这一次我读的是英语版本的。我简直不敢相信我可以不借助手边的字典就读完这本书。自然,它全应归功于你的工作,是你让这本书变得如此易于理解。 -> -- [myd7349](https://github.com/swaroopch/byte_of_python/pull/13) +> 只是想满怀喜悦地大声说一声 _十分感谢你_,感谢你出版了《简明 Python 教程》和《简明 Vim 教程(A Byte of Vim)》。这两本书在我四五年前开始学习编程时对我大有帮助。现在我已经开始开发一项项目,一个开始于很久很久之前的梦想。我只是想对你说一声\_“谢谢你”\_。我将继续前进。你是我一大前进动力的来源。祝你一切顺利。 -- Jocimar - +> 在 3 天里我读完了《简明 Python 教程》。它真的非常有趣。书里面没有一页是无聊的。我希望能够理解 Orca 屏幕阅读器的代码。你的这本书有望成为我开始这项工作的装备。 -- Dattatray -> 我在此通过邮件对你在网络上撰写的《简明 Python 教程》向你表达感谢。在遇到你的这本书之前,我曾花费数月的时间来尝试使用 Python,尽管我通过 pyGame 获得了些许收获,但我还尚未完成一款程序。 -> 感谢你简化了个中类别,使得学习 Python 真的变成了看起来能够达到的目标。现在看来我已经学会了 Python 的基础,并且能够继续下去,实现我的目标——游戏开发。 -> …… -> 再一次感谢你在网络上提供这本结构化、对基础编程很有帮助的教程。它助我对 OOP(面向对象编程)内外都有了足够的理解,这是过去我所学习的两本教材都没能做到的事情。 -> -- [Matt Gallivan](mailto:m_gallivan12@hotmail.com) +> 嗨,《简明 Python 教程》真的是一本非常好的面向 Python 初学者的教材。再次向你祝贺,**好样的**! 我是一名来自中国的有 4 年开发经验的 Java 与 C 开发者。最近,我希望能够完成一些有关 Zim-Wiki 笔记项目的工作,它是通过 pygtk 来实现的。 我用了 6 天时间读完了你的书,现在我可以读写 Python 代码范例了。 感谢您的贡献。 请保持你的热情去让这个世界变得更好,这是来自中国的微小鼓励。 -- Lee - +> 我是来自台湾的 Isen,一名台湾大学电气工程专业的博士生。我想为你这本伟大的书向你表示感谢。我认为它不仅是易于阅读,而且还为 Python 新手提供了全面而完整的内容。促使我阅读你的这本书的原因是我开始在 GNU Radio 框架下工作。 我发现你不介意读者在你的书中向你表示感谢。我十分喜欢你的这本书并对它心怀感激。谢谢你。 -- [Isen I-Chun Chao](mailto:chao926@gmail.com) -> 我要感谢你和你的书 _《简明 Python 教程》_,它是我所能找到的最好的编程学习方式。我的名字叫 Ahmed,15岁,来自埃及。Python 是我学习的第二门编程语言。我曾在学校学习了 Visual Basic 6,但并不是很喜欢它,但现在我十分享受学习 Python 的过程。我编写了一款通讯录程序并且取得了成功。我将开始尝试编写更多程序,也试着去阅读 Python 程序(如果你能告诉我它们的源代码,那会对我大有帮助)。我现在也开始学习 Java,如果你能够告诉我哪里能找到如你的这本书这般优秀的 Java 教程,那真的是帮到我大忙了。感谢你。 -> -- [Ahmed Mohammed](mailto:sedo_91@hotmail.com) - - - -> 由 Swaroop C H 撰写的《简明 Python 教程》这本 110 页的 PDF 教程是针对想要更多地了解 Python 的初学者的绝佳资源。它精心编写,易于跟随,同时还可能是针对 Python 编程的最佳介绍。 -> -- [Drew Ames](http://www.linux.com/feature/126522) - - - -> 昨天我在我的诺基亚 N800 上阅读了《简明 Python 教程》的大部分内容,这是我所遇到过的最简单也最简洁的 Python 介绍。强烈推荐以这本书作为你学习 Python 的起点。 -> -- [Jason Delport](http://paxmodept.com/telesto/blogitem.htm?id=627) - - - -> @swaroopch 撰写的《简明 Vim 教程(Byte of Vim)》与《简明 Python 教程》是我到目前所遇见的最好的技术写作作品。它们都是优秀的作品。#FeelGoodFactor -> -- [Surendran](http://twitter.com/suren/status/12840485454) - - - -> 《简明 Python 教程》是最好的。 -> (对问题“有人能推荐一本优秀且便宜的用来学习 Python 基础的资源吗?”的回答) -> -- [Justin LoveTrue](http://www.facebook.com/pythonlang/posts/406873916788) - - - -> 《简明 Python》十分有帮助……万分感谢。:) -> [Chinmay](https://twitter.com/a_chinmay/status/258822633741762560) - - - -> 一直以来都是《简明 Python 教程》的粉丝——它同时为新程序员与有经验的程序员所编写。 -> -- [Patrick Harrington](http://stackoverflow.com/a/457785/4869) - - - -> 从几天前我开始从你的书中学习 Python……感谢这本优秀的书。它撰写的如此优秀,使我的学习生活 更加容易……现在你有了一名新粉丝——那就是我。:)万分感谢。 -> -- [Gadadhari Bheem](https://twitter.com/Pagal_e_azam/statuses/242865885256232960) - - - -> 在我学习 Python 之前,我已经具有了 Assembly、C、C++、C# 和 Java 的基本编程能力。我想学习 Python 的原因是它十分流行(人们都在谈论它)且功能强大(现实如此)。这本由 Swaroop 先生所撰写的书是一本非常好的教材,它同时面向新程序员与新 Python 程序员。我花了 10 个半天来读完它,十分有帮助! -> -- [Fang Biyi (电气与计算机工程学博士候选人,密歇根州立大学)](mailto:fangbiyi@gmail.com) - - - -> 为这本书向你致谢!! -> 这本书消除了我在 Python 方面诸如面向对象编程等许多问题。 -> 我不觉得我是 OO 方面的专家,但我知道这本书在我迈出的第一与第二步上帮助颇多。 -> 我已经编写了几款 Python 程序,它们确实在我的系统管理员工作中帮我解决了诸多事情。它们都是程序性的,但是在许多人的标准看来它们都如此小巧。 -> 再次感谢这本书。感谢你将它公开在网络上。 -> -- Bob - - - -> 我希望为你撰写的这本我所阅读过的最佳编程书籍向你表示感谢。Python 并不是我使用的第一门语言,但我可以想象它可以拥有的一切可能性。十分感谢你予我这个工具,让我可以创造那些我从未想过我能创造的一切。 -> -- "The Walrus" - - - -> 我希望为你所撰写的 _《简明 Python 教程》(第 2 版与第 3 版)_ 向你表示感谢。它在我整个学习 Python 与编程的历程中弥足珍贵。 -> 不必多言,我是编程世界里的一名新手,我耗费了几个月的时间自己学习从而达到这样的程度。我曾通过 Yotube 教程和其它的一些诸如免费图书的在线教程来学习编程。就在昨天我决定深入学习你的这本书,现在我已经学了开头的几页,这比我在其他任何一本书或教程中所走过的进度都要多。有些事我曾一度感到困惑,但在这本书里这些**优秀**的解释和范例面前都得以解答。我已经等不及去阅读(学习)之后的更多内容了! -> 非常感谢你,不仅是撰写了这本书,还愿意把它通过知识共享协议授权分发(免费地)。感谢上帝,正是如此这样无私的人们帮助并教导了我们其余的这些人。 -> -- Chris - - - -> 在 2011 年时我曾向你写信,那时我才刚刚开始使用 Python,并想为你的教程《简明 Python 教程》向你表示感谢。如果没有它,我想我只会倒在路边。自那时起我已经在我的组织中使用 Python 这门语言编写程序,实现诸多功能,而在未来我相信我能写得更多。无论如何我也不会把自己称作一名高级程序员,但我发现自从我开始使用 Python 后,现在我时不时会收到来自他人的协助请求。我发现,在阅读《简》时,我已经放弃学习 C 和 C\++,因为那些书里在一开始就向我抛出了一个包含增量赋值(Augmented Assignment)的例子。自然,没有任何为何有关在此安排这一赋值的解释,我只能用尽我的头脑去弄清楚纸上印出来的到底是什么内容。这是我所能记得的最令人沮丧的经历,最终我选择了放弃。这并不是意味着 C 或 C\++ 是不可能学会的,抑或我是一个蠢蛋,但它的确意味着我工作中所拥有的文档不会包括任何有关符号或词语的定义,而这些确是在任何介绍中都至关重要的部分。正如计算机不能理解在其所使用的语言的语法之外的计算机词汇或计算机符号一般 ,任何一个领域的新学生如果遇到的全都是没有定义的符号与词汇,他就不能领会其主题所在。你会遇到某一情况下的“蓝屏”。其解决方案简单明了:找到个中词汇与符号并理解其正确的定义,如此一来——你瞧,计算机和学生都可以继续进行他们的任务。你的这本书将二者结合得如此之好,我在其中只有很少的部分无法掌握。因此,谢谢你。我鼓励您继续在书中囊括各术语的完整定义。一旦你有所了解,就能知道Python 的文档是优秀的(于我所见,范例就是它的力量)。但是在许多情况下,为了理解文档,你必须了解在我看来并不需要知道的东西。第三方教程都表示需要借助文档来澄清,它们的成功很大程度上都归功于那些用来描述术语的词语。我已经将你的这本书推荐给其他许多人。有的来自澳大利亚,有的在加勒比,还有一些在美国。它填补了其他人没能填补的位置。我希望你能继续这样好好做下去,并祝愿你在未来的所有成功。 -> -- Nick - - - -> 嗨,我是 Ankush(19岁)。我现在正在面对开始学习 Python 的巨大困难。我尝试了许多教材但它们都过于臃肿,而且也非目标导向;尔后我便遇到这可爱的一本,让我在短时间内就爱上了 Python。十分感谢这本“美妙的一本书”。 -> -- Ankush - - - -> 我要感谢你这本出色的 Python 指南。我是一位分子生物学家(仅有些许编程背景),在我的工作中我需要处理有关 DNA 序列的大数据集,还要分析显微镜图像。对于这些工作,采用 Python 编程对我十分有帮助,如果不是我必须要完成并发表一项历时六年的项目的话。 -> 这样一本教程能够免费提供是邪恶尚未统治世界的确切标志!:) -> -- Luca - - - -> 既然这(Python)将是你学习的第一门编程语言,你应该采用《简明 Python 教程》。它确实为 Python 编程提供了恰当的介绍,且节奏十分适合一般初学者。在此之后最重要的事自然是切实地开始开始练习编写你自己的小程序。 -> -- ["{Unregistered}"](http://www.overclock.net/t/1177951/want-to-learn-programming-where-do-i-start#post_15837176) - - - -> 只是想满怀喜悦地大声说一声 _十分感谢你_,感谢你出版了《简明 Python 教程》和《简明 Vim 教程(A Byte of Vim)》。这两本书在我四五年前开始学习编程时对我大有帮助。现在我已经开始开发一项项目,一个开始于很久很久之前的梦想。我只是想对你说一声_“谢谢你”_。我将继续前进。你是我一大前进动力的来源。祝你一切顺利。 -> -- Jocimar - - - -> 在 3 天里我读完了《简明 Python 教程》。它真的非常有趣。书里面没有一页是无聊的。我希望能够理解 Orca 屏幕阅读器的代码。你的这本书有望成为我开始这项工作的装备。 -> -- Dattatray - - - -> 嗨,《简明 Python 教程》真的是一本非常好的面向 Python 初学者的教材。再次向你祝贺,**好样的**! -> 我是一名来自中国的有 4 年开发经验的 Java 与 C 开发者。最近,我希望能够完成一些有关 Zim-Wiki 笔记项目的工作,它是通过 pygtk 来实现的。 -> 我用了 6 天时间读完了你的书,现在我可以读写 Python 代码范例了。 -> 感谢您的贡献。 -> 请保持你的热情去让这个世界变得更好,这是来自中国的微小鼓励。 -> -- Lee - - - -> 我是来自台湾的 Isen,一名台湾大学电气工程专业的博士生。我想为你这本伟大的书向你表示感谢。我认为它不仅是易于阅读,而且还为 Python 新手提供了全面而完整的内容。促使我阅读你的这本书的原因是我开始在 GNU Radio 框架下工作。 -> 我发现你不介意读者在你的书中向你表示感谢。我十分喜欢你的这本书并对它心怀感激。谢谢你。 -> -- [Isen I-Chun Chao](mailto:chao926@gmail.com) - -还有,本书已被 NASA 采用!NASA 在它们的 [喷气推进实验室(Jet Propulsion Laboratory)](http://dsnra.jpl.nasa.gov/software/Python/byte-of-python/output/byteofpython_html/) 及它们的深空网络计划中采用了本书。 +还有,本书已被 NASA 采用!NASA 在它们的 [喷气推进实验室(Jet Propulsion Laboratory)](http://dsnra.jpl.nasa.gov/software/Python/byte-of-python/output/byteofpython\_html/) 及它们的深空网络计划中采用了本书。 ## 学术课程 本书曾被或正在被各类教育机构当作他们的教材: -- [阿姆斯特丹的自由大学](http://www.few.vu.nl/~nsilvis/PPL/2007/index.html) 的 编程语言原理(Principles of Programming Languages) 课程。 -- [加利福尼亚大学戴维斯分校](http://www.cs.ucdavis.edu/courses/exp_course_desc/10.html) 的 计算机运作的基本概念(Basic Concepts of Computing) 课程。 -- [哈佛大学](http://www.people.fas.harvard.edu/~preshman/python_winter.html) 的 Python 编程(Programming With Python) 课程。 -- [利兹大学](http://www.comp.leeds.ac.uk/acom1900/) 的 编程介绍(Introduction to Programming) 课程。 -- [波士顿大学](http://www.cs.bu.edu/courses/cs108/materials.html) 的 应用程序编程介绍(Introduction to Application Programming) 课程。 -- [俄克拉荷马大学](http://gentry.metr.ou.edu/byteofpython/) 的 气象学信息科技技能(Information Technology Skills for Meteorology) 课程。 -- [密歇根州立大学](http://www.msu.edu/~ashton/classes/825/index.html) 的 地理处理(Geoprocessing) 课程。 -- [爱丁堡大学](http://homepages.inf.ed.ac.uk/ewan/masws/) 的 多代理语义 Web 系统(Multi Agent Semantic Web Systems) 课程。 -- [MIT 开放课程项目(MIT OpenCourseWare)](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00sc-introduction-to-computer-science-and-programming-spring-2011/references/) 的 计算机科学与编程介绍(Introduction to Computer Science and Programming) 课程。 -- 斯洛文尼亚,卢布尔雅那大学的社会科学学院基础编程(Basic Programming)课程 -- [Aleš Žiberna](mailto:ales.ziberna@fdv.uni-lj.si) 说:_“我(以及我的前任)一直使用你的这本书作为课程的主要教材。”_ -- 克罗地亚,扎达尔大学信息科学系的计算机编程介绍(Introduction to programming)课程 -- Krešimir Zauder 说: _“我想告诉你,《简明 Python 教程》是我课上的强制性读物。”_ +* [阿姆斯特丹的自由大学](http://www.few.vu.nl/\~nsilvis/PPL/2007/index.html) 的 编程语言原理(Principles of Programming Languages) 课程。 +* [加利福尼亚大学戴维斯分校](http://www.cs.ucdavis.edu/courses/exp\_course\_desc/10.html) 的 计算机运作的基本概念(Basic Concepts of Computing) 课程。 +* [哈佛大学](http://www.people.fas.harvard.edu/\~preshman/python\_winter.html) 的 Python 编程(Programming With Python) 课程。 +* [利兹大学](http://www.comp.leeds.ac.uk/acom1900/) 的 编程介绍(Introduction to Programming) 课程。 +* [波士顿大学](http://www.cs.bu.edu/courses/cs108/materials.html) 的 应用程序编程介绍(Introduction to Application Programming) 课程。 +* [俄克拉荷马大学](http://gentry.metr.ou.edu/byteofpython/) 的 气象学信息科技技能(Information Technology Skills for Meteorology) 课程。 +* [密歇根州立大学](http://www.msu.edu/\~ashton/classes/825/index.html) 的 地理处理(Geoprocessing) 课程。 +* [爱丁堡大学](http://homepages.inf.ed.ac.uk/ewan/masws/) 的 多代理语义 Web 系统(Multi Agent Semantic Web Systems) 课程。 +* [MIT 开放课程项目(MIT OpenCourseWare)](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00sc-introduction-to-computer-science-and-programming-spring-2011/references/) 的 计算机科学与编程介绍(Introduction to Computer Science and Programming) 课程。 +* 斯洛文尼亚,卢布尔雅那大学的社会科学学院基础编程(Basic Programming)课程 -- [Aleš Žiberna](mailto:ales.ziberna@fdv.uni-lj.si) 说:_“我(以及我的前任)一直使用你的这本书作为课程的主要教材。”_ +* 克罗地亚,扎达尔大学信息科学系的计算机编程介绍(Introduction to programming)课程 -- Krešimir Zauder 说: _“我想告诉你,《简明 Python 教程》是我课上的强制性读物。”_ ## 许可证 @@ -274,36 +138,24 @@ 另请注意: -- 请 *不要* 销售本书的电子或印刷拷贝,除非你明确声明这些拷贝副本并 *非* 来自本书的原作者。 -- 在分发时 *务必* 在文档的介绍性描述或前页、头版中提供回溯至本书原书 {{ book.officialUrl }} 以及本译本 {{book.sctransUrl}} 的链接,并明确指出本书之原文与译本可在上述链接处获取。 -- 除非另有声明,本书所提供的所有代码与脚本均采用 [3-clause BSD License](http://www.opensource.org/licenses/bsd-license.php) 进行授权。 +* 请 _不要_ 销售本书的电子或印刷拷贝,除非你明确声明这些拷贝副本并 _非_ 来自本书的原作者。 +* 在分发时 _务必_ 在文档的介绍性描述或前页、头版中提供回溯至本书原书 \{{ book.officialUrl \}} 以及本译本 \{{book.sctransUrl\}} 的链接,并明确指出本书之原文与译本可在上述链接处获取。 +* 除非另有声明,本书所提供的所有代码与脚本均采用 [3-clause BSD License](http://www.opensource.org/licenses/bsd-license.php) 进行授权。 ## 现在就开始阅读 -你可以通过 {{ book.officialUrl }} 在线阅读本书英文原版。 +你可以通过 [http://python.swaroopch.com/](http://python.swaroopch.com/) 在线阅读本书英文原版。 -本中文译版可通过 {{ book.sctransUrl }} 在线阅读。 +本中文译版可通过 \{{ book.sctransUrl \}} 在线阅读。 ## 购买本书 -本书英文原版的印刷硬拷贝可在 {{ book.buyBookUrl }} 购得,用以获得离线阅读体验,同时也可向本书提供支持以推进后续的开发与改进。 +本书英文原版的印刷硬拷贝可在 [http://www.swaroopch.com/buybook/](http://www.swaroopch.com/buybook/) 购得,用以获得离线阅读体验,同时也可向本书提供支持以推进后续的开发与改进。 本中文译版**没有**发行或许可发行任何印刷硬拷贝。但是其他商业或非商业组织可以在遵守授权协议的前提下自行印刷并发行本书的硬拷贝,这些行为并不需要得到原作者和译者的许可。译者不会因为这些印刷或发行行为获益,亦不对这些未经专门授权的印刷或硬拷贝版本的准确性负责。 -## 下载 - -你可以访问 {{ book.transdownloadurl }} 以获得本书以下格式的下载: - -- [PDF (可在电脑上阅读)](https://www.gitbook.com/download/pdf/book/lenkimo/byte-of-python-chinese-edition) -- [EPUB (可在 iPhone、iPad、电子书阅读器上阅读)](https://www.gitbook.com/download/epub/book/lenkimo/byte-of-python-chinese-edition) -- [Mobi (可在 Kindle 上阅读)](https://www.gitbook.com/download/mobi/book/lenkimo/byte-of-python-chinese-edition) - -上述下载链接由 GitBook 提供。本书在未来可能会有些许修订,GitBook 将在修订提交后自动生成最新版本。如果你打算下载一份电子书版用来离线阅读,建议隔一段时间后回来看看是否有新版。 - -本书采用 Markdown 进行写作。你可以访问 {{ book.transsourceurl }} 以获得本书的源代码内容(用以提交建议、更正或进行重发布)。 -本书英文原版可以在 {{ book.officialUrl }} 在线浏览,其源代码内容可以在 {{ book.sourceUrl }} 获得。 ## 在你使用的语言下阅读本书 -如果你有意在其他人类语言下阅读本书,或为本书提供翻译,请参阅[翻译](./23.translations.md)与[如何翻译](./24.Translation-how-to.md#translation-howto)。 \ No newline at end of file +如果你有意在其他人类语言下阅读本书,或为本书提供翻译,请参阅[翻译](23.translations.md)与[如何翻译](24.Translation-how-to.md#translation-howto)。