1 Python准备
Python的语法非常简单,规则很少,通过讨论一些常用的语句来理解这些规则。
Python只有大约20个不同的命令式语句,其中两种:赋值语句和表达式语句。
例如:
>>> print("hello world")
hello world
该例实际上执行了一个对print()函数求值的语句。这种对函数或者对象的方法求值的语句是很常见的。
另一种之前使用过的语句是赋值语句。Python拥有丰富多样的赋值语句。在大多数情况下,我们为一个变量赋一个值。但是,有时候可能会同时给两个变量赋值,例如:
quotient, remainder = divmod(355, 113)
2 Python脚本和模块文件
为了实现真实的功能,需要编写Python脚本文件。当然,可以在交互命令行下进行试验,但是对于实际工作,还是需要创建文件。编写软件的目的是为数据创建可重复的处理过程。
怎样才能避免语法错误并确保代码符合常规用法?我们需要了解一些常见的编码风格——如何使用空白让设计更加清晰。
也会介绍一些技术方面的注意事项。例如,确保文件保存为UTF-8编码。虽然Python仍然支持ASCII编码,但是对于现代编程来说这不是一个好的选择。我们还需要确保用空格代替制表符。尽量使用Unix换行符会避免很多问题。
大多数文本编辑工具都能正常处理Unix(换行)行尾符和Windows或DOS(回车-换行)行尾符,应当避免使用不能同时支持这两种行尾符的文本编辑工具。
2.1 准备工作
我们需要一个优秀的编程文本编辑器来编辑Python脚本。Python附带提供了一个方便的编辑 器——IDLE,它非常实用,可以让我们在文件和交互命令行之间来回切换,但它算不上是一个极好的编程编辑器。
优秀的编程编辑器非常非常多。首先,为编辑器设置一些默认值。然后,当编辑器正确设置后,为脚本文件创建一个通用的模板。
2.2 实战演练
首先,完成编辑器的常用设置。虽然本实例以Komodo为例,但是基本原则适用于所有编辑器。设置完编辑偏好后,就可以创建脚本文件了。
(1) 打开编辑器,查看偏好(preferences)页。
(2) 找到首选文件编码设置,设置为UTF-8。Komodo Edit的偏好设置在国际化(internationalization)选项卡上。
(3) 找到缩进设置。如果有用空格代替制表符选项,就选中该选项。对于Komodo Edit,实际上做的是相反操作——取消选中空格优先于制表符(prefer spaces over tabs)选项。
规则:使用空格,而不是制表符。
同时,设置每个缩进为4个空格。这是Python代码的典型设置,可以让代码包含多个级别的缩进,始终保持代码非常紧凑。
确定文件保存为UTF-8编码,以及缩进使用的是空格而不是制表符之后,就可以创建一个示例脚本文件了。
(1) 大多数Python脚本文件的第一行如下所示:
#!/usr/bin/env python3
该行代码建立了Python和正在编写的文件之间的关联。
对于Windows,文件名与应用程序的关联是通过一个Windows控制面板中的某个设置完成的。在“默认程序”控制面板中,有个“设置关联”面板。在这个控制面板中可以看到 .py文件绑定到了Python程序上。文件关联通常由安装程序设定,很少需要更改或手动设置。
Windows开发者可以随意编写这个序言行(shabang行)。当Mac OS X和Linux使用者从GitHub下载项目时,他们会非常高兴。
(2) 序言行之后,应该有一个三引号括起来的文本块。这是要创建的文件的文档字符串(docstring)。文档字符串不是代码,只是用来解释文件的。
'''
A summary of this script.
'''
由于Python三引号字符串的长度可以无限大,因此可以根据需要随意编写。文档字符串应当是描述脚本或库模块的主要载体。文档字符串甚至可以包含说明脚本或模块使用方法的示例。
(3) 编写脚本的核心处理,即确实执行的那一部分。在这一部分中,我们可以编写所有用于完成处理的语句。但是目前我们不会实现所有语句,而是先使用以下语句作为占位符:
print('hello world')
通过上述语句,我们的脚本实现了一些简单的处理。随后的实例将介绍其他常用语句,这些语句通常用于创建函数和类定义,以及编写语句来使用函数和类。
在脚本的顶层,所有的语句都从左边开始,并且必须在一行内完成。有一些复杂的语句包含嵌套的语句块。这些内部的语句块必须缩进。通常,可以按Tab键来缩进,因为我们设置缩进为4个空格。
通过上述步骤创建的示例脚本如下:
#!/usr/bin/env python3
'''
My First Script: Calculate an important value.
'''
print(355/113)
2.3 工作原理
与其他语言不同,Python中的样板代码(boilerplate)很少,只有一行固定内容(overhead),甚至连 # !/usr/bin/env python3行都是可选的。
为什么要把编码设置为UTF-8?整个语言其实只使用原始的128 ASCII字符就可以工作。
但是,我们经常会发现ASCII编码的限制。将编辑器设置为使用UTF-8编码会更容易处理各种情况。通过这个设置,可以简单地使用任何有意义的字符。如果以UTF-8编码保存项目,就可以使用像μ这样的字符作为Python变量。
如果文件以UTF-8编码保存,那么下面的代码在Python中就是合法的:
π=355/113
print(π)
在Python中,选择使用空格或者制表符,并保持一致是很重要的。它们或多或少都是不可见的,混合使用很容易导致混乱。建议使用空格。
若编辑器使用4个空格的缩进,可以使用键盘上的Tab键插入4个空格。代码将正确地对齐,缩进能够展示出语句之间的嵌套关系。
最开始的#!行是一个注释:#和行尾之间的所有内容都会被忽略。像bash和ksh这样的操作系统shell程序将检查文件的第一行,以查看文件包含的内容。前几个字节有时被称为魔术(magic),因为shell正在监视它们。shell程序查找#!的两个字符序列,以识别负责此数据的程序。我们更喜欢使用/usr/bin/env来启动Python程序。我们可以利用这个特性通过env程序设定特定的Python的环境配置。
2.4 补充内容
“Python标准库”的文档部分来源于模块文件中的文档字符串。在模块中编写复杂的文档字符串是常见的做法。像Pydoc和Sphinx这样的工具可以将模块的文档字符串重新格式化为优雅的文档。我们将在不同的实例中进行讨论。
另外,单元测试用例也可以包含在文档字符串中。类似doctest的工具可以从文档字符串中提取示例,并执行代码以查看文档中的答案是否与运行代码所找到的答案相符。本书大部分示例代码都是通过doctest验证的。
3个引号括起来的文档字符串优于#注释。#和行尾之间的文本即为注释,每行注释都需加上 #,使用时需谨慎。文档字符串中间的文本不限制行数,所以用得更多。
在Python 3中,有时在脚本文件中会看到如下代码:
color = 355/113 # type: float
类型推断系统可以通过注释 # type: float确定程序实际执行时可以出现的各种数据类型。更多信息请参阅PEP 484(Python Enhancement Proposal 484,https://www.python.org/dev/peps/pep-0484/)。
有时,文件还包含另外一些固定内容。VIM编辑器允许在文件中保留编辑偏好,这通常被称为模式行(modeline),我们常常必须通过在 ~/.vimrc文件中包含set modeline设置来启用模式行。
一旦启用了模式行,就可以在文件末尾加入一个特殊的 # vim注释来配置VIM。
下面是一个典型的用于Python的模式行:
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
根据这个设置,当敲击Tab键时,Unicode u+0009 TAB字符将转换为8个空格,我们将移动4个空格。这个设置是文件中携带的,不必再做任何VIM设置。
3 编写长行代码
在很多情况下,我们需要编写长行代码,这种代码阅读体验比较差。许多人喜欢将一行代码的长度限制为不超过80个字符,因为这种做法符合众所周知的平面设计原则:短代码可读性强。关于每行的长度众说纷纭,但65个字符通常被认为是最理想的。参见http://webtypography.net/2.1.2。
虽然较短的行阅读起来更容易,但长短全凭个人喜好。长语句是一个常见的问题。怎样才能把很长的Python语句分解为更易管理的部分?
3.1 准备工作
冗长而又难以处理的长语句很常见。例如:
>>> import math
>>> example_value = (63/25) * (17+15*math.sqrt(5)) / (7+15*math.sqrt(5))
>>> mantissa_fraction, exponent = math.frexp(example_value)
>>> mantissa_whole = int(mantissa_fraction*2**53)
>>> message_text = 'the internal representation is
{mantissa:d}/2**53*2**{exponent:d}'.format(mantissa=mantissa_whole,
exponent=exponent)
>>> print(message_text)
the internal representation is 7074237752514592/2**53*2**2
上述代码包含一个很长的公式和一个需要注入值的长格式字符串,在纸书上看起来很糟糕,在计算机屏幕上看起来也很烦琐。
我们不能简单地将Python语句分解成块。语法规则清楚地表明,语句必须在单个逻辑(logical)行上完成。
术语“逻辑行”提示了应该如何解决长语句问题。Python区分逻辑行和物理行,我们可以利用这些语法规则分解长语句。
3.2 实战演练
为了提高长语句的可读性,Python提供了多种包装长语句的方法。
可以在行的结尾使用\续行。
根据Python的语法规则,语句可以跨越多个物理行,因为()、[]和{}字符必须平衡。除了使用()和\,还可以利用Python自动连接相邻字符串字面量的方式来创建一个更长的字面量,(“a” “b”)和"ab"是一样的。
在某些情况下,可以通过将中间结果赋给单独的变量来分解语句。
本实例的各个部分将详细介绍上述方法。
使用反斜杠将长语句分解为逻辑行
这种技术的背景信息如下:
>>> import math
>>> example_value = (63/25) * (17+15*math.sqrt(5)) / (7+15*math.sqrt(5))
>>> mantissa_fraction, exponent = math.frexp(example_value)
>>> mantissa_whole = int(mantissa_fraction*2**53)
Python允许使用\断行。
(1) 将整个语句写在一个长行上,即使很混乱。
>>> message_text = 'the internal representation is
{mantissa:d}/2**53*2**{exponent:d}'.format(mantissa=mantissa_whole,
exponent=exponent)
(2) 在逻辑中断的位置插入\。有时候可能并没有真正完美的逻辑中断。
>>> message_text = 'the internal representation is \
... {mantissa:d}/2**53*2**{exponent:d}'.\
... format(mantissa=mantissa_whole, exponent=exponent)
>>> message_text
'the internal representation is 7074237752514592/2**53*2**2'
为此,\必须是行内的最后一个字符。\之后甚至不能有空格。空格是很难察觉到的,因此不鼓励使用\。
尽管这种技术存在一定的缺陷,但\总是非常有效的。我们可以将其视为使一行代码更具可读性的最后手段。
使用()字符将长语句分解为合理的部分
(1) 整个语句写在一行,即使看起来很混乱。
>>> import math
>>> example_value1 = (63/25) * (17+15*math.sqrt(5)) /
(7+15*math.sqrt(5))
(2) 添加额外的()字符并不会改变语句的值,但允许将表达式分成多行。
>>> example_value2 = (63/25) * ( (17+15*math.sqrt(5)) /
(7+15*math.sqrt(5)) )
>>> example_value2 == example_value1
True
(3) 在()字符内打断行。
>>> example_value3 = (63/25) * (
... (17+15*math.sqrt(5))
... / ( 7+15*math.sqrt(5))
... )
>>> example_value3 == example_value1
True
匹配()字符的技术是相当强大的,适用于各种情况,所以使用广泛,值得强烈推荐。
添加额外的()字符很容易实现。在极少数情况下,无法添加()字符,或者添加()字符不能改进语句,这时可以转而使用\将语句分成段。
使用字符串字面量连接合并
我们可以使用组合字符串字面量的另一个规则组合()字符。这对于很长而又复杂的格式字符串特别有效。
(1) 在()字符中包裹一个长字符串值。
(2) 将字符串拆分为子字符串。
>>> message_text = (
... 'the internal representation '
... 'is {mantissa:d}/2**53*2**{exponent:d}'
... ).format(
... mantissa=mantissa_whole, exponent=exponent)
>>> message_text
'the internal representation is 7074237752514592/2**53*2**2'
长的字符串很容易被分成相邻的部分。一般来说,当片段被()字符包围时,这是最有效的。然后可以使用尽可能多的物理换行符。这种方法仅限于有特别长的字符串值的情况。
将中间结果赋值给单独的变量
这种技术的背景信息如下:
>>> import math
>>> example_value = (63/25) * (17+15*math.sqrt(5)) / (7+15*math.sqrt(5))
可以把这个语句分成三个中间值。
(1) 识别整个表达式中的子表达式,将它们赋给变量。
>>> a = (63/25)
>>> b = (17+15*math.sqrt(5))
>>> c = (7+15*math.sqrt(5))
这个步骤通常很简单,可能需要注意代数运算,以便于确定合理的子表达式。
(2) 用创建好的变量替换子表达式。
>>> example_value = a * b / c
这个步骤是用变量对原始复杂子表达式所做的必要的文本替换。
这些变量没有被赋予描述性名称。在某些情况下,子表达式包含一些可以捕获有意义名称的语义。在这个例子中,我们没有很好地理解表达式,以提供非常有意义的名称,而是选择了短的、随意的标识符。
3.3 工作原理
Python语言参考手册区分了逻辑行和物理行。逻辑行包含一个完整的语句,它可以通过行连接(line joining)技术跨越多个物理行。Python语言参考手册称这种技术为显式行连接(explicit line joining)和隐式行连接(implicit line joining)。
使用\的显式行连接有时非常有用。但是因为很容易忽视\后面的不可见字符,所以一般不鼓励使用这种方法。这是行连接最后的备用方法。
使用()的隐式行连接适用于许多情况。这种技术通常符合表达式的结构,所以鼓励使用。()字符也可以是必需的语法的一部分,例如,()字符已经是print()函数语法的一部分。利用()字符分解长语句的示例如下:
>>> print(
... 'several values including',
... 'mantissa =', mantissa,
... 'exponent =', exponent
... )
3.4 补充内容
表达式广泛应用于Python语句。任何表达式都可以添加()字符,很灵活。
然而有时候可能会遇到并不具体包含一个表达式的长语句。最显著的例子就是import语句,它可以变得很长很长,但是并不使用任何带括号的表达式。
语言设计者允许使用()字符,这样就可以把一长串名称分为多个逻辑行:
>>> from math import (sin, cos, tan,
... sqrt, log, frexp)
在这个例子中,()字符显然不是表达式,它只是一种语法,使该语句与其他语句相一致。