1. 开始之前
Python 提供了很多数据结构,也支持大型程序,远超 shell 脚本或批处理文件的功能。Python 还提供比 C 语言更多的错误检查,而且作为一种“超高级语言”,它有高级的内置数据类型,比如灵活的数组和字典。
Python 允许你划分程序模块,在其他的 Python 程序中重用。它内置了很多的标准模块,你可以在此基础上开发程序。
Python 是一种解释型语言,在程序开发阶段可以为你节省大量时间,因为不需要编译和链接。
2. Python 解释器
2.1 调用解释器
在 Python 可用的机器上,Python 解释器通常放在 /usr/local/bin/python3.7;把 /usr/local/bin 放到你 Unix shell 的搜索路径当中 , 这样键入命令:python3.7
就能运行了。
另一种启动解释器的方式是 python -c command [arg] ...
,其中 command
要换成想执行的指令,就像命令行的 -c 选项。由于 Python 代码中经常会包含对终端来说比较特殊的字符,通常情况下都建议用英文单引号把 command
括起来。
有些 Python 模块也可以作为脚本使用。可以这样输入:python -m module [arg] ...
,这会执行 module 的源文件,就跟你在命令行把路径写全了一样。
在运行脚本的时候,有时可能也会需要在运行后进入交互模式。这种时候在文件参数前,加上选项 -i 就可以了。
2.2 传入参数
如果可能的话,解释器会读取命令行参数,转化为字符串列表存入 sys
模块中的 argv
变量中。执行命令 import sys
你可以导入这个模块并访问这个列表。这个列表最少也会有一个元素。
如果没有给定输入参数,sys.argv[0]
就是个空字符串。
如果脚本名是标准输入,sys.argv[0]
就是 '-'。
使用 -c command
时,sys.argv[0]
就会是 '-c'。
如果使用选项 -m module
,sys.argv[0]
就是包含目录的模块全名。
在 -c command
或 -m module
之后的选项不会被解释器处理,而会直接留在 sys.argv
中给命令或模块来处理。
2.3 解释器的运行环境
默认情况下,Python 源码文件以 UTF-8 编码方式处理。如果不使用默认编码,要声明文件所使用的编码,文件的第一行要写成特殊的注释。语法如下所示:
# -*- coding: encoding -*-
关于第一行规则的一种例外情况是,源码以 UNIX "shebang" 行开头。这种情况下,编码声明就要写在文件的第二行。例如:
1 2 | #!/usr/bin/env python3
# -*- coding: cp1252 -*-
|
3. Python 的非正式介绍
3.1 数字
整数有 int
类型,有小数部分的有 float
类型。
除法运算 (/) 永远返回浮点数类型。
如果要得到一个整数结果(忽略小数部分)你可以使用 // 运算符。
如果要计算余数,可以使用 %。
在Python中,可以使用 ** 运算符来计算乘方。
Python 也内置对复数的支持,使用后缀 j 或者 J 就可以表示虚数部分。
在交互模式下,上一次打印出来的表达式被赋值给变量 _。
3.2 字符串
字符串有多种形式,可以使用单引号('……'),双引号("……")都可以获得同样的结果。反斜杠 \ 可以用来转义。
在交互式解释器中,输出字符串是在引号中的,特殊字符是前置了反斜杠的。函数 print()
会产生更方便阅读的输出,就是不使用引号,也不转义特殊字符。
如果你不希望前置了 \ 的字符转义成特殊字符,可以使用原始字符串方式,在引号前添加 r 即可。
字符串字面值可以跨行连续输入。一种方式是用三重引号:"""...""" 或 '''...'''。字符串中的回车换行会自动包含到字符串中,如果不想包含,在行尾添加一个 \ 即可。
字符串可以用 + 进行连接(粘到一起),也可以用 * 进行重复。相邻的两个或多个 字符串字面值(引号引起来的字符)将会自动连接到一起,只能对两个字面值这样操作,变量或表达式不行。
字符串是可以被 索引(下标访问)的,第一个字符索引是 0。单个字符并没有特殊的类型,只是一个长度为一的字符串。索引也可以用负数,这种会从右边开始数。注意 -0 和 0 是一样的,所以负数索引从 -1 开始。
使用过大的索引会产生一个错误,但是,切片中的越界索引会被自动处理。
Python 中的字符串不能被修改,它们是 immutable
的。因此,向字符串的某个索引位置赋值会产生一个错误。
内建函数 len()
返回一个字符串的长度。
3.3 列表
列表,可以通过方括号括起、逗号分隔的一组值得到。一个列表可以包含不同类型的元素,但通常使用时各个元素类型相同。
列表也支持索引和切片。所有的切片操作都返回一个新列表,这个新列表包含所需要的元素。
列表同样支持拼接操作 +。
列表是一个 mutable
类型,就是说,它自己的内容可以改变。
在列表结尾,通过 append()
方法 添加新元素。
内建函数 len()
返回一个列表的长度。
4. 其他流程控制工具
4.1 if 语句
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
4.2 for 语句
>>> for w in words:
... print(w, len(w))
如果在循环内需要修改序列中的值(比如重复某些选中的元素),推荐你先拷贝一份副本。对序列进行循环不代表制作了一个副本进行操作。
4.3 range() 函数
给定的终止数值并不在要生成的序列里;range(10)
会生成 10 个值,并且是以合法的索引生成一个长度为 10 的序列。range
也可以以另一个数字开头,或者以指定的幅度增加(甚至是负数;有时这也被叫做 '步进')。
range()
所返回的对象在许多方面表现得像一个列表,但实际上却并不是。此对象会在你迭代它时基于所希望的序列返回连续的项,但它没有真正生成列表,这样就能节省空间。
我们说这样的对象是可迭代的,也就是说,适合作为函数和结构体的参数,这些函数和结构体期望在迭代结束之前可以从中获取连续的元素。我们已经看到 for
语句就是这样一个迭代器。函数 list()
是另外一个;它从可迭代对象中创建列表。
>>> list(range(5))
[0, 1, 2, 3, 4]
4.4 break 和 continue 语句,以及循环中的 else 子句
循环语句可能带有一个 else
子句;它会在循环遍历完列表 (使用 for
) 或是在条件变为假 (使用 while
) 的时候被执行,但是不会在循环被 break
语句终止时被执行。
4.5 pass 语句
pass
语句什么也不做。
4.6 定义函数
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
函数体的第一个语句可以(可选的)是字符串文字;这个字符串文字是函数的文档字符串或 docstring
。
4.7 函数定义的更多形式
4.7.1 参数默认值
默认值是在定义过程中在函数定义处计算的,所以
i = 5
def f(arg=i):
print(arg)
i = 6
f()
会打印 5。
重要警告: 默认值只会执行一次。这条规则在默认值为可变对象(列表、字典以及大多数类实例)时很重要。比如,下面的函数会存储在后续调用中传递给它的参数:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
这将打印出
[1]
[1, 2]
[1, 2, 3]
4.7.2 关键字参数
在函数调用中,关键字参数必须跟随在位置参数的后面。
传递的所有关键字参数必须与函数接受的其中一个参数匹配,它们的顺序并不重要。
不能对同一个参数多次赋值。
4.7.3 任意的参数列表
最不常用的选项是可以使用任意数量的参数调用函数。这些参数会被包含在一个元组里(参见 元组和序列 )。在可变数量的参数之前,可能会出现零个或多个普通参数。
4.7.4 解包参数列表
使用 * 运算符编写函数调用以从列表或元组中解包参数。
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
4.7.5 Lambda 表达式
Lambda
函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。
从语义上来说,它们只是正常函数定义的语法糖。与嵌套函数定义一样,lambda
函数可以引用包含范围的变量。
返回两个参数的和:lambda a, b: a+b
4.7.6 文档字符串
以下是有关文档字符串的内容和格式的一些约定。
第一行应该是对象目的的简要概述。这一行应以大写字母开头,以句点结尾。
如果文档字符串中有更多行,则第二行应为空白,从而在视觉上将摘要与其余描述分开。
Python 解析器不会从 Python 中删除多行字符串文字的缩进,因此处理文档的工具必须在需要时删除缩进。这是使用以下约定完成的。文档字符串第一行之后的第一个非空行确定整个文档字符串的缩进量。(我们不能使用第一行,因为它通常与字符串的开头引号相邻,因此它的缩进在字符串文字中不明显。)然后从字符串的所有行的开头剥离与该缩进 "等效" 的空格。缩进的行不应该出现,但是如果它们出现,则应该剥离它们的所有前导空格。应在扩展标签后测试空白的等效性(通常为 8 个空格)。
4.7.7 函数标注
函数标注是关于用户自定义函数中使用的类型的完全可选元数据信息。
函数标注以字典的形式存放在函数的 __annotations__
属性中,并且不会影响函数的任何其他部分。
形参标注的定义方式是在形参名称后加上冒号,后面跟一个表达式,该表达式会被求值为标注的值。
返回值标注的定义方式是加上一个组合符号 ->,后面跟一个表达式,该标注位于形参列表和表示 def 语句结束的冒号之间。
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
4.8 编码风格
- 使用 4 个空格缩进,不要使用制表符
- 换行,使一行不超过 79 个字符
- 使用空行分隔函数和类,以及函数内的较大的代码块
- 如果可能,把注释放到单独的一行
- 使用文档字符串
- 在运算符前后和逗号后使用空格,但不能直接在括号内使用:
a = f(1, 2) + g(3, 4)
- 类和函数命名的一致性;规范是使用 CamelCase 命名类,lower_case_with_underscores 命名函数和方法。始终使用 self 作为第一个方法参数的名称
- 如果你的代码旨在用于国际环境,请不要使用花哨的编码
- 不要在标识符中使用非 ASCII 字符
5. 数据结构
5.1 列表的更多特性
list.append(x)
在列表的末尾添加一个元素。相当于 a[len(a):] = [x]。list.extend(iterable)
使用可迭代对象中的所有元素来扩展列表。相当于 a[len(a):] = iterable 。list.insert(i, x)
在给定的位置插入一个元素。第一个参数是要插入的元素的索引。list.remove(x)
移除列表中第一个值为 x 的元素。如果没有这样的元素,则抛出 ValueError 异常。list.pop([i])
删除列表中给定位置的元素并返回它。如果没有给定位置,a.pop()
将会删除并返回列表中的最后一个元素。list.clear()
删除列表中所有的元素。相当于del a[:]
。list.index(x[, start[, end]])
返回列表中第一个值为 x 的元素的从零开始的索引。如果没有这样的元素将会抛出 ValueError 异常。list.count(x)
返回元素 x 在列表中出现的次数。list.sort(key=None, reverse=False)
对列表中的元素进行排序(参数可用于自定义排序)。list.reverse()
反转列表中的元素。list.copy()
返回列表的一个浅拷贝。相当于 a[:]。
像 insert
,remove
或者 sort
方法,只修改列表,没有打印出返回值——它们返回默认值 None
。
5.2 元组和序列
一个元组由几个被逗号隔开的值组成。输入时圆括号可有可无,不过经常会是必须的(如果这个元组是一个更大的表达式的一部分)。
元组是 immutable
(不可变的),其序列通常包含不同种类的元素,并且通过解包(这一节下面会解释)或者索引来访问(如果是 namedtuples
的话甚至还可以通过属性访问)。
列表是 mutable
(可变的),并且列表中的元素一般是同种类型的,并且通过迭代访问。
一个特殊的问题是构造包含 0 个或 1 个元素的元组:为了适应这种情况,语法有一些额外的改变。空元组可以直接被一对空圆括号创建,含有一个元素的元组可以通过在这个元素后添加一个逗号来构建(圆括号里只有一个值的话不够明确)。
5.3 集合
Python 也包含有 集合 类型。集合是由不重复元素组成的无序的集。它的基本用法包括成员检测和消除重复元素。集合对象也支持像 联合,交集,差集,对称差分等数学运算。
花括号或 set()
函数可以用来创建集合。注意:要创建一个空集合你只能用 set()
而不能用 {}
,因为后者是创建一个空字典。
5.4 字典
与以连续整数为索引的序列不同,字典是以关键字为索引的,关键字可以是任意不可变类型,通常是字符串或数字。如果一个元组只包含字符串、数字或元组,那么这个元组也可以用作关键字。但如果元组直接或间接地包含了可变对象,那么它就不能用作关键字。
一对花括号可以创建一个空字典:{}
。dict()
构造函数可以直接从键值对序列里创建字典。
可以用 del
来删除一个键值对。
对一个字典执行 list(d)
将返回包含该字典中所有键的列表,按插入次序排列 (如需其他排序,则要使用 sorted(d)
)。要检查字典中是否存在一个特定键,可使用 in
关键字。
5.5 循环的技巧
当在字典中循环时,用 items()
方法可将关键字和对应的值同时取出。
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
当在序列中循环时,用 enumerate()
函数可以将索引位置和其对应的值同时取出。
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
当同时在两个或更多序列中循环时,可以用 zip()
函数将其内元素一一匹配。
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
当逆向循环一个序列时,先正向定位序列,然后调用 reversed()
函数。
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
如果要按某个指定顺序循环一个序列,可以用 sorted()
函数,它可以在不改动原序列的基础上返回一个新的排好序的序列。
有时可能会想在循环时修改列表内容,一般来说改为创建一个新列表是比较简单且安全的。
5.6 深入条件控制
比较操作符 in
和 not in
校验一个值是否在(或不在)一个序列里。操作符 is
和 is not
比较两个对象是不是同一个对象,这只跟像列表这样的可变对象有关。
比较操作可以传递。例如 a < b == c
会校验是否 a 小于 b 并且 b 等于 c。
比较操作可以通过布尔运算符 and
和 or
来组合,并且比较操作(或其他任何布尔运算)的结果都可以用 not
来取反。在它们之中,not
优先级最高, or
优先级最低。
布尔运算符 and
和 or
也被成为 短路 运算符:它们的参数从左至右解析,一旦可以确定结果解析就会停止。当作用于普通值而非布尔值时,短路操作符的返回值通常是最后一个变量。
6. 模块
模块是一个包含 Python 定义和语句的文件。文件名就是模块名后跟文件后缀 .py 。在一个模块内部,模块名(作为一个字符串)可以通过全局变量 __name__
的值获得。
6.1 有关模块的更多信息
模块可以包含可执行的语句以及函数定义。这些语句用于初始化模块。它们仅在模块第一次在 import
语句中被导入时才执行。(当文件被当作脚本运行时,它们也会执行。)
出于效率的考虑,每个模块在每个解释器会话中只被导入一次。因此,如果你更改了你的模块,则必须重新启动解释器,或者,如果它只是一个要交互式地测试的模块,请使用 importlib.reload()
,例如:
import importlib
importlib.reload(modulename)
6.2 以脚本的方式执行模块
当你用下面方式运行一个 Python 模块:
python fibo.py <arguments>
模块里的代码会被执行,就好像你导入了模块一样,但是 __name__
被赋值为 "__main__"
。 这意味着通过在你的模块末尾添加这些代码:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
你既可以把这个文件当作脚本又可当作一个可调入的模块来使用。