骑麦兜看落日

[Code]Python学习手册_第三部分_语句和语法

字数统计: 27.2k阅读时长: 119 min
2018/07/30 Share

第十章 Python语句简介

本章介绍了语句和代码块代码编写的通用规则

  • 一般情况下每行编写一条语句
  • 嵌套代码块中的所有语句都缩进相同的量
  • 一些例外情况有连续行以及单行测试和循环

重访Python程序结构

Python的语法实质是语句表达式组成的

  • 程序由模块构成
  • 模块包含语句
  • 语句包含表达式
  • 表达式建立并处理对象

模块包含语句,语句则管理模块

语句使用并引导表达式处理对象

语句可以生成对象

Python的语句

语句 用途 例子
赋值 创建引用值 a, b, c = ‘good’, ‘bad’, ‘ugly’
调用 执行函数 log.write(“spam, ham”)
打印调用 打印对象 print(‘The Killer’, joke)
if/elif/else 选择动作 if “python” in text: print(text)
for/else 序列迭代 for x in mylist: print(x)
while/else 一般循环 while X > Y: print(‘hello’)
pass 空占位符 while True: pass
break 循环退出 while True: if exittest(): berak
continue 循环继续 while True: if skiptest(): continue
def 函数和方法 def f(a, b, c=1, *d): print(a+b+c+d[0])
return 函数结果 def f(a, b, c=1, *d): return a+b+c+d[0]
yield 生成器函数 def gen(n): for i in n: yield i*2
global 命名空间 x = ‘old’; def function(): global x,y; x = ‘new’
nonlocal 命名空间 def outer(): x = ‘old’;def function(): nonlocal x;x = ‘new’
import 模块访问 import sys
from 属性访问 from sys import stdin
class 创建对象 class Subclass(Surperclass): staticData = [];def method(self): pass
try/except/finally 捕捉异常 try: action();except: print(‘action error’)
raise 触发异常 raise EndSearch(location)
assert 调试检查 assert X>Y, ‘X too small’
with/as 环境管理器 with open(‘data’) as myfile: process(myfile)
del 删除引用 del data[k];del data[i:j];del obj.attr;del variable
  • 赋值语句以不同的语法形式呈现:基本的、序列的、拓展的
  • print不是一个保留字,也不是一套语句,而是一个内置的含糊调用
  • yield是一个表达式而不是一条语句

两个if的故事

1
2
3
4
if(x > y){
x = 1;
y = 2;
}

这可能是C、C++、Java、JavaScript或Perl的语句

1
2
3
if x > y:
x = 1
y = 2

Python语法成分比较少

Python增加了什么

Python中的语法成分:

1
2
header line:
Nested statement block

Python的所有复合语句都有相同的一般形式:首行以:结尾,首行下一行嵌套的代码按缩进的格式书写

Python删除了什么

与类C语言程序比较

括号是可选的

1
if x < y

Python可以省略括号

终止行就是终止语句

1
x = 1

Python中一行的结束会自动终止该行的语句

Python允许在每个语句末使用分号

缩进的结束就是代码块的结束

1
2
3
if x > y:
x = 1
y = 2

Python中把嵌套块里所有的语句向右缩进相同的距离,确定代码块的开头和结尾

缩进指嵌套语句至左侧的所有空白

Python不在乎怎么缩进(空格或制表符)或者缩进多少(任意多个空格或是制表符),并且两个嵌套代码块的缩进可以完全不同

为什么使用缩进语法

使用缩进语法可以根据程序的逻辑结构,以垂直对齐的方式来组织程序代码,让程序更一致并具有可读性,从而具备重用性和可维护性

索引没有绝对的标准,常见的是每层四个空格或一个制表符

不可以在同一段Python代码中混合使用制表符和空格

1
2
3
4
5
if x:
if y:
statement1
else:
statement2

几个特殊实例

Python的语法模型

  • 一行的结束就是终止该行语句(没有分号)
  • 嵌套语句是代码块并且与实际的缩进相关(没有大括号)

语句规则的特殊情况

1
a = 1; b = 2; print(a + b)	# Three statements on one line

可以将多个简单语句写入一行,由语句限定符;隔开

复合语句不可以与其他语句写入一行

1
2
3
4
5
6
7
8
9
mlist = [111,
222,
333]
X = (A + B +
C + D)
if (A == 1 and
B == 2 and
C == 3):
print('spam' * 3)

括在()[]{}中的语句可以让语句的范围横跨多行直到遇见闭合的括号

括号中的复合语句同样可以跨越多行

尽管连续行可以不缩进,为了程序的可读性也应该对齐

1
2
X = A + B + \	# An error-prone alternative
C + D

当上一行以反斜线结束时,可以在下一行继续

但是这种方法不再提倡,因为关注并维护反斜线比较困难,而且这种操作相当脆弱

代码块规则特殊实例

1
if x > y: print(x)

复合语句的主体可以出现在Python的首行:之后

  • 复合语句不可以跟在:之后
  • 较复杂的语句不可以跟在:之后
  • 复合语句的附带部分,例如ifelse部分不可以跟在:之后

语句体可以由几个简单语句组成并用;隔开


简短实例:交互循环

一个简单的交互式循环

读取用户键盘输入数据的循环并打印每次读取的结果

1
2
3
4
while True:
reply = input('Enter text:')
if reply == 'stop': break
print(reply.upper())

从用户那里读取一行并用大写字母打印,直到输入stop

  • 这个程序利用了Python的while循环,它是Python最通用的循环语句

    它的组成为: while关键字之后跟一个结果为TrueFalse的表达式,再接一个当顶端测试为真时不停地迭代的嵌套代码块

  • input内置函数用于通用控制台输出,它打印可选的参数字符串作为提示,并返回用户输入的回复字符

  • 利用嵌套代码块特殊规则的单行if语句也在这里出现: if语句体出现在:之后的首行,而并不是在首行的下一行缩进

  • Python的break语句用于立即退出循环,也就是完全跳出循环语句而程序会继续循环之后的部分

源文件的结束或是一个缩进较少的语句能够终止这个循环体块

对用户输入数据做数学运算

1
2
3
4
5
6
7
#!/usr/bin/env python

while True:
reply = input('Enter text:')
if reply == 'stop': break
print(int(reply) ** 2)
print('Bye')

除非表达式里的对象类型都是数字,否则Python不会在表达式中转换对象类型

用户的输入返回到脚本一定是一个字符串,只有手动转换为数字类型对象才能求幂

用测试输入数据来处理错误

1
2
3
4
5
6
7
8
while True:
reply = input('Enter text:')
if reply == 'stop': break
elif not reply.isdigit():
print('Bad!' * 8)
else:
print(int(reply) ** 2)
print('Bye')

当输入无效时内置int函数会返回ValueError

isdigit方法可以检查字符串的内容是否为数字

if语句完整结构为:if关键字后接测试以及匹配的代码块,一个或多个可选的elif测试以及代码块,以及一个可选的else和匹配的代码块

ifelif以及else属于if语句的一部分,因为它们垂直对齐

Python执行首次测试为真所匹配的代码块,当所有的测试都为假则执行else部分

用try语句处理错误

try语句用来捕捉并处理错误

1
2
3
4
5
6
7
8
9
10
while True:
reply = input('Enter text:')
if reply == 'stop': break
try:
num = int(reply)
except:
print('Bad!' * 8)
else:
print(int(reply) ** 2)
print('Bye')

try语句组成:try关键字后接尝试运行的代码,except部分以及异常处理代码,else部分以及没有引发异常的处理代码

tryexcept以及else属于try语句的一部分,因为它们全都缩进在同一层次

嵌套代码三层

1
2
3
4
5
6
7
8
9
10
11
12
13
while True:
reply = input('Enter text:')
if reply == 'stop':
break
elif not reply.isdigit():
print('Bad!' * 8)
else:
num = int(reply)
if num < 20:
print('low')
else:
print(num ** 2)
print('Bye')

多层嵌套需要再往右缩进


本章习题

  1. 类C语言中需要哪三项在Python中省略了的语法成分

    类C语言需要在一些语句中的测试两侧使用圆括号,需要在每个语句末尾有分号,以及嵌套代码块周围有大括号

  2. Python中的语句一般是怎样终止的

    一行的结尾就是该行语句的终止

    如果一个以上的语句出现在同行上,可以使用分号终止

    如果一个语句跨过数行,可以用语法上的闭合括号终止这一行

  3. 在Python中,嵌套代码块内的语句一般是如何关联在一起的

    嵌套代码块中的语句都得缩进相同数目的制表符或空格

  4. 你怎么让一条语句跨过多行

    语句可以横跨多行,只要将其封闭在圆括号内、方括号内或大括号内即可

    当Python遇到一行含有一对括号中的闭合括号,语句就会结束

  5. 你怎么在单个行上编写复合语句

    复合语句的主体可以移到开头行的冒号后面,但前提是主体只由非复合语句构成

  6. 有什么理由要在Python语句末尾输入分号呢

    只有当你需要把一列以上的语句挤进一行代码时

    即使是这种情况下,也只有当所有语句都是非复合时,才行得通,此外因为这样会让程序代码难以阅读,所以不建议这么做

  7. try语句是用来做什么的

    try语句是用于在Python脚本中捕捉和恢复异常(错误)的

    这通常是程序中自行检查错误的方法之一

  8. Python初学者最常犯的编写代码错误是什么

    忘记在复合语句开头行末尾输入冒号,是初学者最常犯的错误


第十一章 赋值、表达式和打印

  • 这一章开始探索赋值语句、表达式预计打印,从而深入研究Python语句
  • 学习这些语句一些可选的替代形式
  • 研究变量名的语法、重定向技术以及各种要避免的常见错误

赋值语句

基本形式:赋值目标 = 赋值对象,赋值目标可以是变量或对象元素,赋值对象可以是任何可以计算得到的对象的表达式

赋值语句特性

  • 赋值语句建立对象引用值

    赋值语句会把对象引用值存储在变量名或数据结构的元素内

  • 变量名在首次赋值时会被创建

    Python会在首次将值赋值给变量时创建变量名

    有些(并非全部)数据结构元素也会在赋值时创建(例如,字典中的元素,一些对象属性)

    赋值后变量名出现在表达式时,会被其所引用的值取代

  • 变量名在引用前必须先赋值

    使用尚未进行赋值的变量名是一种错误,Python会引发异常,而不是返回某种模糊的默认值

  • 执行隐式赋值的一些操作

    在Python中,赋值语句会在许多情况下使用

    除了=显式赋值语句,模块导人、函数和类的定义、for循环变量以及函数参数全都是隐式赋值运算,赋值语句的工作原理都相同,都是在运行时把变量名和对象的引用值绑定起来

赋值语句的形式

运算 解释
spam = ‘Spam’ 基本形式
spam, ham = ‘yum’, ‘YUM’ 元组赋值运算(位置性)
[spam, ham] = [‘yum’, ‘YUM’] 列表赋值运算(位置性)
a, b, c, d = ‘spam’ 序列赋值运算,通用性
a, *b = ‘spam’ 拓展的序列解包
spam = ham = ‘lunch’ 多目标赋值运算
spams += 42 增强赋值运算
  • 基本形式

    把一个变量名(或数据结构元素)绑定到单个对象上

  • 元组及列表分解赋值

    当在=左边编写元组或列表时,Python会按照位置把右边的对象和左边的目标从左至右相配对

    从内部实现上来看,Python会先在右边制作元素的元组,所以这通常被称为元组分解赋值语句

    • 序列赋值语句

      元组和列表赋值语句统一为序列赋值语句的实例

      任何变量名的序列都可赋值为任何值的序列,Python会按位置一次赋值一个元素

  • 拓展的序列解包

    *可以匹配序列的剩下部分

  • 多重目标赋值

    Python将最右边的对象的引用值赋值给左边所有目标

  • 增强赋值语句

    以简洁的方式结合表达式和赋值语句的简写形式

    每个二元表达式运算符都有增强赋值语句

序列赋值

语句执行时,Python会建立临时的元组来存储右侧变量原始的值

1
2
3
4
5
6
7
8
>>> nudge = 1
>>> wink = 2
>>> A, B = nudge,wink # Tuple assignment
>>> A, B # Like A = nudge;B = wink
(1, 2)
>>> [C, D] = [nudge, wink] # List assignment
>>> C, D
(1, 2)

Python把赋值运算符右侧元组内的值和左侧元组内的变量互相匹配,然后每一次赋一个值

1
2
3
4
5
6
(1, 2)
>>> nugge = 1
>>> wink = 2
>>> nudge, wink = wink, nudge # Tuples:swaps values
>>> nudge, wink # Like T = nudge;nudge = wink;wink = T
(2, 1)

分解赋值语句不用自行创建临时变量就可以交换两变量的值,右侧的元组会自动记住先前的变量的值

1
2
3
4
5
6
>>> [a, b, c] = (1, 2, 3)	# Assign tuple of values to list of names
>>> a, c
(1, 3)
>>> (a, b, c) = "ABC" # Assign string of characters to tuple
>>> a, c
('A', 'C')

Python中原始的元组和列表赋值语句已被通用化,只要长度相等,右侧可以接受任何可迭代对象

通非常情况下Python会按位置由左至右把右侧序列中的元素赋值给左侧序列中的变量

高级序列赋值语句模式

序列赋值语句=可以两侧混合相匹配的序列类型,但是右侧元素的数目需要和左侧变量的数目相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> string = 'SPAM'
>>> a, b, c, d = string # Same number on both sides
>>> a, d
('S', 'M')
>>> a, b, c = string # Error if not
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)
>>> a, b, c = string[0], string[1], string[2:] # Index and slice
>>> a, b, c
('S', 'P', 'AM')
>>> a, b, c = list(string[:2]) + [string[:2]] # Slice and concatenate
>>> a, b, c
('S', 'P', 'SP')
>>> a, b = string[:2] # Same,but simpler
>>> c = string[2:]
>>> a, b, c
('S', 'P', 'AM')
>>> (a, b), c = string[:2], string[2:] # Nested sequence
>>> a, b, c
('S', 'P', 'AM')
>>> ((a, b), c) = ('SP', 'AM')
>>> a, b, c # Paired by shape and position
('S', 'P', 'AM')

赋值目标中的项数和主体的数目必须一致

可以使用分片使序列赋值语句更通用

赋值嵌套序列时,左侧对象的序列嵌套的形状必须符合右边对象的性质,Python为每一层嵌套赋值

  • 赋值嵌套序列在for循环中将循环项赋值给循环头部给定的目标
  • 赋值嵌套序列在Python 2.6中用于函数参数列表
1
2
3
4
5
red, green, blue = range(3)
red, blue
(0, 2)
>>> range(3) # Use list(range(3)) in Python 3.0
range(0, 3)

序列解包赋值语句赋值一系列整数给一组变量

range函数产生连续整数列表

元组赋值语句在循环中将序列分割为开头和剩余两部分

Python 3.0中的拓展序列解包

*在赋值目标中使用来引用一个包含所有没有赋值给其他名称的项的序列

扩展的解包的实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
>>> seq = [1, 2, 3, 4]
>>> a, b, c, d = seq
>>> print(a, b, c, d)
1 2 3 4
>>> a, b = seq
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
>>> a, *b = seq
>>> a
1
>>> b
[2, 3, 4]
>>> *a, b = seq
>>> a
[1, 2, 3]
>>> b
4
>>> a, *b, c = seq
>>> a
1
>>> b
[2, 3]
>>> c
4
>>> a, b, *c = seq
>>> a
1
>>> b
2
>>> c
[3, 4]

序列赋值要求左边的目标名称数目有右边的主体中的项数完全一致,否则得到一个错误

当使用一个*的时候,左边的目标中的项数不需要与主体序列的长度匹配

*可以出现在目标中的任何地方

  • 出现在最前面时,匹配其他目标名称所匹配项的前边的项
  • 出现在最后面时,匹配最后没有匹配的项
  • 出现在中间时,收集其他目标名称所匹配项之间的所有向
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> a, *b = 'spam'
>>> a, b
('s', ['p', 'a', 'm'])
>>> a, *b, c = 'spam'
>>> a, b, c
('s', ['p', 'a'], 'm')
>>> S = 'spam'
>>> S[0], S[1:] # Slices are type-specific,* assignment always returns a list
('s', 'pam')
>>> S[0], S[1:3], S[3]
('s', 'pa', 'm')
>>> L = [1, 2, 3, 4]
>>> while L:
... front, *L = L
... print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
>>> L = [1, 2, 3, 4]
>>> while L:
... front, L = L[0], L[1:]
... print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []

拓展的序列解包语法对于任何序列类型都有效

一个序列解包赋值总是返回多个匹配项的列表,分片把相同类型的一个序列作为分片的对象返回

边界情况

1
2
3
4
5
6
7
8
9
10
>>> seq = [1, 2, 3, 4]
>>> a, b, c, *d = seq
>>> print(a, b, c, d)
1 2 3 [4]
>>> a, b, c, d, *e = seq
>>> print(a, b, c, d, e)
1 2 3 4 []
>>> a, b, *c, d, e = seq
>>> print(a, b, c, d, e)
1 2 [] 3 4

*即使只匹配当项也会返回一个列表

*如果没有剩下的内容可以匹配则返回一个空的列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> seq = [1, 2, 3, 4]
>>> a, *b, c, *d = seq
File "<stdin>", line 1
SyntaxError: two starred expressions in assignment
>>> a, b = seq
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
>>> *a = seq
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple
>>> *a, = seq
>>> a
[1, 2, 3, 4]

引发错误情况

  • 有多个*
  • 值少了而没有*
  • *自身没有编写到一个列表中

一个有用的便利形式

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> seq = [1, 2, 3, 4]
>>> a, *b = seq # First,rest
>>> a, b
(1, [2, 3, 4])
>>> a, b = seq[0], seq[1:] # First,rest:traditional
>>> a, b
(1, [2, 3, 4])
>>> *a, b = seq # Rest,last
>>> a, b
([1, 2, 3], 4)
>>> a, b = seq[:-1], seq[-1] # Rest,last:traditional
>>> a, b
([1, 2, 3], 4)

拓展的解包相比较索引和分片更容易编写

应用于for循环

拓展的序列赋值对于for循环语句中的循环变量有效

每次迭代中,Python直接把下一个值的元组分配给目标名称的元组

多目标赋值语句

1
2
3
4
5
6
>>> a = b = c = 'spam'
>>> a, b, c
('spam', 'spam', 'spam')
>>> c = 'spam'
>>> b = c
>>> a = b

多目标赋值语句就是把所有提供的变量名都赋值为最右侧的对象的引用

多目标赋值以及共享引用

多目标赋值的多个变量共享一个被赋值对象

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> a = b = 0
>>> b = b + 1
>>> a, b
(0, 1)
>>> a = b = []
>>> b.append(42)
>>> a, b
([42], [42])
>>> a = []
>>> b = []
>>> b.append(42)
>>> a, b
([], [42])

对于不可变类型,共享引用不会出现问题,由于不支持原处修改.对于其中一个变量的修改只会改变当前变量的引用

对于可变类型,当在原处修改其中一个变量时也会修改其他引用的变量

为避免共享引用的问题应避免使用多目标赋值语句

增强赋值语句

1
2
3
4
5
6
7
8
9
10
11
>>> x = 1
>>> x = x + 1 # Traditional
>>> x
2
>>> x += 1 # Augmented
>>> x
3
>>> S = "spam"
>>> S += "SPAM" # Implied concatenation
>>> S
'spamSPAM'

增强赋值语句适用于任何支持隐式二元表达式的类型

增强赋值语句
X += Y X &= Y X -= Y X |= Y
X *= Y X ^= Y X /= Y X >>= Y
X %= Y X <<= Y X **= Y X //= Y

增强赋值语句优点

  • 程序员输入减少

  • 左侧只需计算一次

    X+=Y中,X可以是复杂的对象表达式,在增强形式中,则只需计算一次,在完整形式X = X + Y中,X出现两次,必须执行两次,因此,增强赋值语句通常执行得更快。

  • 优化技术会自动选择

    对于支持原处修改的对象而言,增强形式会自动执行原处的修改运算,而不是相比来说速度更慢的复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> L = [1, 2]
>>> L = L + [3] # COncatenate:slower
>>> L
[1, 2, 3]
>>> L.append(4) # Faster,but in-place
>>> L
[1, 2, 3, 4]
>>> L = L + [5, 6] # Concatenate:slower
>>> L
[1, 2, 3, 4, 5, 6]
>>> L.extend([7, 8]) # Faster,but in-place
>>> L
[1, 2, 3, 4, 5, 6, 7, 8]
>>> L += [9, 10]
>>> L
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

在两种情况下,合并对共享对象引用产生的副作用可能会更小,但是,通常会比对等的原处形式运行得更慢

  • 合并操作必须创建一个新的对象,把左侧的复制到列表中,然后再把右侧的复制到列表中
  • 原处方法调用直接在一个内存块末尾添加项

使用增强赋值语句拓展列表时,Python会自动调用较快的extend方法而不是+运算

增强赋值以及共享引用

1
2
3
4
5
6
7
8
9
10
>>> L = [1, 2]
>>> M = L # L and M reference the same object
>>> L = L + [3, 4] # ConcatenatIon makes a new object
>>> L, M # Changes L but not M
([1, 2, 3, 4], [1, 2])
>>> L = [1, 2]
>>> M = L
>>> L += [3, 4] # But += really means extend
>>> L, M # M sees the in-place change too!
([1, 2, 3, 4], [1, 2, 3, 4])

对于支持原处修改的对象而言,+=增强形式会自动执行原处的修改运算,而不像 +生成新的对象

变量命名规则

基本规则

  • 语法:(下划线或字母) + (任意数目的字母、数字或下划线)

  • 区分大小写

  • 禁止使用保留字

    定义的变量名不能和Python语言中有特殊意义的名称相同

Python 3.0中的保留字

保留字
False class finally is return
None continue for lambda try
True def from nonlocal while
and del global not with
as elif if or yield
assert else import pass
break except in raise

Python保留字大多数小写,无法对其进行赋值运算

TrueFlaseNone是大小写混合,除了作为保留字,还可以作为分配给对象的值

import语句无法导入模块名为保留字的模块

命名规则

解释器的命名惯例并非必要的规则,但一般在实际中都会遵守

  • 以单一下划线开头的变量名_X不会被from module import *语句导入
  • 前后有下划线的变量名_X_是系统定义的变量名,对解释器有特殊意义
  • 以两下划线开头、但结尾没有两个下划线的变量名__X是类的本地(“压缩”)变量
  • 通过交互模式运行时,只有单个下划线的变量名(_)会保存最后表达式的结果

程序员遵循的其他管理

  • 类变量名以一个大写字母开头
  • 模块变量名以小写字母开头
  • self在类中一般有特殊的角色

变量名没有类型,但对象有

  • 对象有类型(例如,整数或列表),并且可能是可变的活不可变的
  • 变量名只是对象的引用值,没有不可变的概念,也没有相关联的类型信息

Python的废弃协议

保留字在一种语言的各个阶段是变化的,当一种新的功能可能会影响到已有的代码的时候,Python通常会使其成为可选的,并且将其发布为废弃的,以便在正式使用该功能之前的一个或多个发布中提出警告


表达式语句

Python中可以使用表达式作为语句(本身占一行),但是无法保存

只有当表达式工作并作为附加的效果表达式语句才有意义

  • 调用函数和方法

    有些函数和方法会做很多工作,而不会有返回值因,所以可以用表达式语句调用这些函数

  • 在交互模式提示符下打印值

    Python会在交互模式命令行中响应输人的表达式的结果,作为输入print语句的简写方法

常见Python表达式语句

运算 解释
spam(eggs, ham) 函数调用
spam.pam(eggs) 方法调用
spam 在交互模式下解释器内打印变量
print(a, b, c, sep=’’) Python 3.0中的打印操作
yield x ** 2 产生表达式的语句

函数和方法的调用卸载函数/方法变量名后的括号内,具有零或多个参数的对象(计算对象的表达式)

1
2
3
4
>>> x = print('spam')	# print is a function call expression in 3.0
spam
>>> print(x) # But it is coded as an expression statement
None

没有返回值的函数默认返回None

Python中表达式可作为语句出现,但语句不能用作表达式,例如,赋值语句=不允许嵌入到表达式所以不会出现将==达成=而意外修改变量的值

表达式语句和在原处的修改

1
2
3
4
5
6
7
>>> L = [1, 2]
>>> L.append(3) # Append is an in-place change
>>> L
[1, 2, 3]
>>> L = L.append(4) # BuT append returns None,not L
>>> print(L) # So we lose our list
None

当把可于原处修改的对象的方法重新赋值时会返回None


打印操作

print语句可以实现把一个或多个对象转换为其文本表达形式,打印到标准输出流或另一个类似文件的流

  • 文件对象方法

    文件写入方法是把字符串写入到任意的文件

    print默认地把对象打印到stdout流,添加了一些自动的格式化

    和文件方法不同,在使用打印操作的时候,不需要把对象转换为字符串

  • 标准输出流

    标准输出流(stdout)是发送一个程序的文本输出的默认的地方,加上标准输入流和错误流,它是脚本启动时所创建的3种数据连接中的一种

    标准输出流通常映射到启动Python程序的窗口,除非它已经在操作系统的shell中重定向到一个文件或管道

  • 由于标谁输出流在Python中可以作为内置的sys模块中的stdout文件对象使用(例如,sys .stdout),用文件的写入方法调用来模拟print成为可能,然而,print很容易使用,这使得很容易就能把文本打印到其他文件或流

Python 3.0和Python 2.6的差异

  • 在Python 3.X中,打印是一个内置函数,用关键字参数来表示特定模式
  • 在Python 2.X中,打印是语句,拥有自己的特定语法

Python 3.0的print函数

print函数属于表达式语句

print函数使用标准的函数调用语法,返回None

调用格式

print(\[object,...]\[, sep=' ']\[, end='\n'][, file=sys.stdout])

方括号中的项是可选的,=后面的值为参数默认值,必须使用name=value语法来根据名称而不是位置传递参数

参数

  • 要打印的每个对象的文本表示,通过把该对象传递给str函数调用获得

  • sep是在每个对象的文本之间插入的一个字符串,它默认是一个单个的空格

    传递一个空字符串将会抑制分隔符

  • end是添加在打印文本末尾的一个字符串,它默认是一个\n

    传递一个空字符串将会避免在打印的文本的末尾移动到下一个输入行

  • file指定了文本将要发送到的文件、标谁流或者其他类似文件的对象,它默认的是sys.stdout

    带有类似文件的write(string)方法的任何对象都可以传递,真正的文件应该已经打开

Python 3.0的print函数的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> print()													# Display a blank line

>>> x = 'spam'
>>> y = 99
>>> z = ['eggs']
>>> print(x, y, z) # Print 3 objects per defaults
spam 99 ['eggs']
>>> print(x, y, z, sep='') # Suppress separator
spam99['eggs']
>>> print(x, y, z, sep=', ') # Custon separator
spam, 99, ['eggs']
>>> print(x, y, z, end='') # Suppress line break
spam 99 ['eggs']>>>
>>> print(x, y, z, end=''); print(x, y, z) # Two prints,same output line
spam 99 ['eggs']spam 99 ['eggs']
>>> print(x, y, z, end='...\n') # Custom line end
spam 99 ['eggs']...
>>> print(x, y, z, sep='...', file=open('data.txt', 'w')) # Point to a file
>>> print(x, y, z) # Back to stdout
spam 99 ['eggs']
>>> print(open('data.txt').read()) # Display file text
spam...99...['eggs']

>>> print(x, y, z, sep='...', end='!\n') # Multiple keywords
spam...99...['eggs']!
>>> print(x, y, z, end='!\n', sep='...') # Order doesn't matter
spam...99...['eggs']!

print函数不需要把待打印对象转换为字符串

默认情况下,print调用在打印的对象之间添加一个空格,要取消这个空格,给sep关键字参数传入一个空字符串,或者传入一个自己所选择的替代分隔符

默认情况下,print添加一个行末字符来结束输出行,可以通过向end关键字参数传入一个空字符串来抑制这一点并避免换行,或者可以传入一个自己的不同的终止符(包含一个\n符号来手动地换行)

file直接把文本打印到一个输出文件或者其他的可兼容对象

可以组合关键字参数来指定分隔符和行末字符串,它们可以以任何顺序出现, 但是必须出现在所有要打印的对象的后面

1
2
3
4
5
>>> text = '%s: %-.4f, %05d' % ('Result', 3.14159, 42)
>>> print(text)
Result: 3.1416, 00042
>>> print('%s: %-.4f, %05d' % ('Result', 3.14159, 42))
Result: 3.1416, 00042

可以提前构造格式化字符串或在print()函数中使用字符串工具打印格式化字符串

Python 2.6print语句

Python 2.6中的打印使用具有独特和具体的语法的一条语句

语句形式

Python 2.6语句 Python 3.0对等形式 说明
print x, y print(x ,y) 把对象的文本形式打印到sys.stdout,并且在各项之间添加一个空格,在末尾添加一个换行
pirnt x, y, print(x, y, end=’’) 不会在文本末尾添加换行
print >> afile, x, y print(x, y, file=afile) 把文本发送到myfile.write而不是sys.stdout.write

Python 2.6 print语句应用

1
2
3
4
5
6
7
8
9
10
>>> x = 'a'
>>> y = 'b'
>>> print x, y
a
>>> print x, y,;print x, y
a b a b
>>> print x + y
ab
>>> print '%s...%s' % (x, y)
a...b

默认情况下,Python 2.6 print语句在逗号分隔的项之间添加空格,并且在当前输出行的末尾添加一个换行

省略换行字符需要在print语句后添加一个,

取消各项之间的空格使用字符串合并工具

可以使用字符串格式化工具构建一个输出字符串

打印流重定向

打印默认发送到标准输出流

可以在脚本中重定向一个脚本的

Python的”Hello World”程序

1
2
3
4
5
6
7
8
9
>>> print('hello world')	# Print a string object in 3.0 
hello world
>>> print 'hello world' # Print a string object in 2.6
hello world
>>> 'hello world' # Interactive echoes
'hello world'
>>> import sys # Printint the hard way
>>> sys.stdout.write('hello world\n')
hello world

print语句提供了sys.stdout对象的简单接口,并加入了默认的格式设置

重定向输出流

重设sys.stdout到目标文件可以使基于print的程序重定向

1
print(X, Y)		# Or,in 2.6:print X, Y

等价于

1
2
import sys
sys.stdout.write(str(X) + ' ' + str(Y) + '\n')

sys.stdout通过str执行字符串转换,通过+增加一个分隔符和一个换行,调用write方法输出

1
2
3
>>> import sys
>>> sys.stdout = open('log.txt', 'a') # Redirects prints to a file
>>> print(x, y, x) # Shows up in log.txt

print函数可以把sys.stdout重新赋值给标准输出流以外的东西

sys.stdout重设成已打开的文件对象( 采用附加模式a)后,程序中任何地方的print语句都会将文字写至重定向文件的末尾,而不是原始的输出流

  • print语句将会持续地调用sys.stdoutwrite方法,无论sys.stdout当时引用的是什么
  • 可以将sys.stdout重设为费文件的对象,只要该对象有预期的协议(write方法)

自动化流重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import sys
>>> temp = sys.stdout # Save for restoring later
>>> sys.stdout = open('log.txt', 'a') # Redirect prints to a file
>>> print('spam') # Prints go to file,not here
>>> print(1, 2, 3)
>>> sys.stdout.close() # Flush output to disk
>>> sys.stdout = temp # Restore original stream
>>> print('back here') # Prints show up here again
back here
>>> print(open('log.txt').read()) # Result of earlier prints
('a', 'b', 'a')
spam
1 2 3

sys.stdout是普通的文件对象,可以存储它,需要时恢复它

1
2
3
4
5
6
7
8
9
>>> log = open('log.txt', 'w')
>>> print(1, 2, 3, file=log) # 2.6:print>>log,1,2,3
>>> print(4, 5, 6, file=log)
>>> log.close()
>>> print(7, 8, 9) # 2.6:print 7,8,9
7 8 9
>>> print(open('log.txt').read())
1 2 3
4 5 6

file关键字允许一个单个的print调用将其文本发送给一个文件的write方法,而不是重设sys.stdout

1
2
3
4
5
6
>>> import sys
>>> sys.stderr.write(('Bad!' * 8) + '\n')
Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad!
33
>>> print('Bad!' * 8, file=sys.stderr)
Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad!

file关键字可以用于把错误消息打印到标准错误流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> X = 1; Y = 2
>>> print(X, Y) # Print:the easy way
1 2
>>> import sys # Print:the hard way
>>> sys.stdout.write(str(X) + ' ' + str(Y) + '\n')
1 2
4
>>> print(X, Y, file=open('temp1', 'w')) # Redirect text to file
>>> open('temp2', 'w').write(str(X) + ' ' + str(Y) + '\n') # Send to file manually
4
>>> print(open('temp1', 'rb').read()) # Binary mode for bytes
b'1 2\n'
>>> print(open('temp2', 'rb').read())
b'1 2\n'

print操作通常是显示文本的最佳选择

版本独立的打印

可以使用Python 3.0的2to3转换脚本自动将Python 2.6的print语句转换为Python 3.0函数调用

可以在Python 2.6代码中添加from __future__ import print_function支持print函数调用的变体

可以在Python 2.6中添加外围括号作为Python 3.0的print函数,但是打印多个值会产生一个元组对象

可以使用格式化表达式或方法调用返回一个字符串对象

为什么要注意print和stdout

print语句和sys.stdout之同的等效使得可以把sys.stdout重新赋值为用户定义的对象(提供和文件相同的方法,就像write),因为print语句只是传送文本给sys.stdout.write方法,可以把sys.stdout赋值为一个对象,而由该对象的write方法可以任意处理方式,可以通过这个对象捕捉程序中打印的文本

1
2
3
4
5
6
7
class FileFaker:
def write(self, string):
# Do something with printed text in string

import sys
sys.stdout = FileFaker()
print(someObjects) # Sends to class weite method

本章习题

  1. 举出三种可以把三个变量赋值成相同值的方式

    你可以使用多重目标赋值语句A=B=C=0、序列赋值语句A,B,C=0,0,0或者单独行上的多重赋值语句A=0,B=0,C=0

    就后者的技术而言,就像第10章介绍过的,你也可以把三个单独的语句用分号合并在同一行上A=0;B=0;C=0

  2. 将三个变量赋值给可变对象时,可能需要注意什么

    如果你用这种方式赋值:

    A=B=C=[]
    这三个变量名都会引用相同对象,所以对其中一个变量名进行在原处的修改也会影响其他变量名

    只有对列表或字典这类可变对象进行在原处的修改时,才会如此

    对不可变对象而言,诸如数字和字符串,则不涉及此问题

  3. L = L.sort()有什么错误

    列表sort方法就像append方法,也是对主体列表进行在原处的修改:返回None,而不是其修改的列表

    赋值给L,会把L设为None,而不是排序后的列表

    内建函数sorted会排序任何序列,并传回具有排序结果的新列表因为这并不是在原处的修改,可以将其结果赋值给变量名

  4. 怎么使用print语句来向外部文件发送文本

    要把一个单个的打印操作打印到一个文件,可以使用Python 3.0的print(X,file=F)调用形式

    或者在打印前把sys.stdout指定为手动打开的文件并在之后恢复最初的值

    你也可以用系统shell的特殊语法,把程序所有的打印文字重定向到一个文件,但这是在Python的范围之外的内容


第十二章 if测试和语法规则

  • 本章介绍Python的if语句,也就是根据测试结果,从一些备选的操作中进行选择的主要语句
  • 复习Python的一般语法规则
  • 深人地探索真值测试运算
  • 还会学习如何在Python中编写多路分支以及if/else表达式

if语句

if语句是选取要执行的操作的语句

if语句是复合语句,可以包含其他语句,包括其他if语句在内

通用格式

1
2
3
4
5
6
if <test1>:			# if test
<statements1> # Associated block
elif <test2>: # Optional elifs
<statements2>
else: # Optional else
<statement3>

形式为if测试,后面跟着一个或多个可选的elif测试,以及一个最终可选的else

所有测试部分和else部分都有一个相关的嵌套语句块,缩进在首行下面

if语句执行时,Python会测试第一个计算结果为真的代码块,所有测试都为假时执行else

基本例子

1
2
3
4
5
6
7
8
9
10
>>> if 1:
... print('true')
...
true
>>> if not 1:
... print('true')
... else:
... print('false')
...
false

if语句除了if测试及其相关联的语句外,其他部分都是可选的

1是布尔真值,测试会永远成功,处理假值结果则可用not

多路分支

1
2
3
4
5
6
7
8
9
>>> x = 'killer rabbit'
>>> if x =='roger':
... print("how's jessica?")
... elif x == 'bugs':
... print("what's up doc>")
... else:
... print('Run away!Run away!')
...
Run away!Run away!

Python会执行第一次测试为真的语句下面的嵌套语句,如果所有测试都为假时,就执行else部分

每一个代码段中都可以嵌套一个以上的语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
>>> choice = 'ham'
>>> print({'spam': 1.25, # A dictionary-based 'switch'
... 'ham': 1.99, # Use has_key or get for default
... 'eggs': 0.99,
... 'bacon': 1.10}[choice])
1.99
>>> if choice == 'spam':
... print(1.25)
... elif choice == 'ham':
... print(1.99)
... elif choice == 'eggs':
... print(0.99)
... elif choice == 'bacon':
... print(1.10)
... else:
... print('Bad choice')
...
1.99
>>> branch = {'spam': 1.25,
... 'ham': 1.99,
... 'eggs': 0.99}
>>> print(branch.get('spam', 'Bad choice'))
1.25
>>> print(branch.get('bacon', 'Bad choice'))
Bad choice
>>> choice = 'bacon'
>>> if choice in branch:
... print(branch[choice])
... else:
... print('Bad choice')
...
Bad choice

Python中没有switchcase语句,可以写成一系列if/elif测试或者对字典进行索引运算或搜索列表

if语句的else分句按默认情况处理,字典默认值可以用get方法或异常捕捉,还可以用if语句的in成员测试

字典中可以包含函数来编写更复杂动作

字典和列表可在运行时创建,比硬编码的if语句更有灵活性


Python语法规则

Python语法的特性

  • 语句是逐个运行的,除非不这样编写

    Python一般按照次序从头到尾执行文件中嵌套块中的语句

    if等语句会是的解释器在程序内跳跃,称为控制流程语句

  • 块和语句的边界会自动检测

    Python使用首行下的语句缩进把嵌套块内的语句组合起来

    Python语句一般不以分号终止,一行的末尾通常就是该行缩写语句的结尾

  • 复合语句=首行 + : + 缩进语句

    复合语句首行以冒号终止,再接一个或多个嵌套语句,且在首行下缩进

    缩进语句叫做块

  • 空白行、空格以及注释通常都会忽略

    文件中空白行将忽略,但在交互模式提示符下不会

    语句和表达式中的空格几乎都忽略(除了字符串常量中以及用在缩进时)

    注释总是忽略,以#开头.延伸至该行的末尾

  • 文档字符串会忽略,但会保存并由工具显示

    文档字符串是一种注释,和#不同的是会在运行时保留下来

    文档字符串是出现在程序文件和一些语句顶端的字符串,在运行时会自动附加在对象上,并能由文档工具显示

代码块分隔符

Python自动以行缩进检测块的边界,缩进至右侧相同距离的所有语句属于同一块的代码

缩进是语法模型的一部分,而不是编程风格

1
2
3
4
5
6
7
x = 1
if x:
y = 2
if y:
print('block2')
print('block1')
print('block0')

一个嵌套块以再往右缩进的语句开始,碰到缩进量较少的语句或文件末尾结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	x = 'SPAM'						# Error:first line indented
if 'rubbery' in 'shrubbery':
print(x * 8)
x += 'NI' # Error:unexpected indentation
if x.endwith('Ni'):
x *=2
print(x) # Error:inconsistent indentation
x = 'SPAM'
if 'rubbery' in 'shrubbery':
print(x * 8)
x += 'NI'
if x.endswith('Ni'):
x *=2
print(x)

通常顶层代码必须于第一栏开始,嵌套块可以从任何栏开始

Python不在乎怎么缩进(空格或制表符)或者缩进多少(任意多个空格或是制表符),并且两个嵌套代码块的缩进可以完全不同,惯例使用4个空格或者一个制表符

Python只在乎缩进是否一致(数量一致,空格或制表符一致)

避免混合使用制表符和空格:Python 3.0中心的错误检查

一段代码块中可以使用空格或制表符来缩进,但是不能混合使用

语句的分隔符

Python语句一般在其所在行的末尾结束

一些特殊情况

当语句太长、难以单放在一行时,有些特殊规则可使其位于多行

  • 如果使用语法括号对,语句就可以横跨数行

    在封闭的(){}[]配对中写代码,可以在多行继续输入语句直到到括号

    多行的语句可在任何缩进层次开始

  • 如果语句以反斜线结束,就可横跨数行

    在前一行的末尾加上\可以在下一行继续输入

    可能会导致忘掉\产生语法错误,或下一行被看做新语句产生不可预期的结果

  • 字符串常量有特殊规则

    三重引号字符串块可以横跨数行

    相邻的字符串常量是隐式地连接起来的,使用括号对可以横跨多行

  • 其他规则

    可以用分号终止语句把一个以上的简单语句挤进单个行中

    注释和空白行也能出现在文件的任意处

    #注释可以出现在行的末尾

1
2
3
L = ["Good",
"Bad",
"Ugly"] # Open pairs may span lines

可以把受界限限制的内容放在任意数目的行中

括号可以存放表达式、函数参数、函数的首行、元组和生成器表达式以及可以放到花括号中的任何内容(字典、集合常量、集合和字典解析)

1
2
3
4
5
6
7
8
if a == b and c == d and \
d == e and f == g:
print('olde') # Backslashes allow continuations
if (a == b and c == d and
d == e and f == g):
print('olde') # But parentheses usually do too
x = 1 + 2 + 3 \ # Omitting the \ makes this very different
+ 4

\可以在下一行继续,但是可以改为把语句包含在圆括号中

漏掉\可能会产生错误或不可预期的结果

1
x = 1; y = 2; print(x)

Python允许使用;在相同行上编写一个以上的非复合语句,但可读性会下降

1
2
3
4
5
6
7
8
S = """
aaaa
bbbb
cccc"""

S = ('aaaa'
'bbbb' # Comments here are ignored
'cccc')

三重引号字符串可以跨多行,和开放对规则一起使用时,相邻的字符串常量也可以横跨多行

在三重引号中添加注释不会被忽略,在相邻的字符串常量之间添加注释会忽略

1
if 1: print('hello')	# Simple statement

可以把复合语句主体上移到首行,只要该主体是简单语句


真值测试

布尔运算符特性

  • 任何非零数字或非空对象都为真
  • 数字零、空对象以及特殊对象None都被认作是假
  • 比较和相等测试会递归地应用在数据结构中
  • 比较和相等测试会返回TrueFalse
  • 布尔andor运算符会返回真或假的操作对象

布尔表达式运算符

  • X and Y

    如果X和Y都为真,就是真

  • X Or Y

    如果X或Y为真,就是真

  • not X

    如果X为假,就是真

X和Y为任何真值或者返回真值的表达式

1
2
>>> 2 < 3, 3 < 2	# Less-than:return True or False
(True, False)

比较会返回TrueFalse作为结果`

1
2
3
4
5
6
7
8
9
10
11
12
>>> 2 or 3, 3 or 2	# Return left operand if true
(2, 3) # Else,return right operand(true or false)
>>> [] or 3
3
>>> [] or {}
{}
>>> 2 and 3, 3 and 2 # Return left operand if flase
(3, 2) # Else,return right operand(true or false)
>>> [] and {}
[]
>>> 3 and []
[]

and测试与or测试返回运算符两侧的对象而不是简单的True或`False

or测试与and测试执行短路计算,即求出结果后就会使表达式其余部分短路(终止)

or测试由左至右求算操作对象,在找到第一个真值操作对象的地方终止并返回操作对象,否则返回右侧操作对象

and测试由左至右求算操作对象,在找到第一个假值操作对象的地方终止并返回操作对象,否则返回右侧操作对象


if/else三元表达式

1
2
3
4
5
6
7
if X:
A = Y
else:
A = Z
A = Y if X else Z
A = ((X and Y) or Z)
A = [Z, Y][bool(X)]

A = Y if X else Z三元表达式执行短路运算.只有当X为真,Python才会执行表达式Y,而只有当X为假,才会执行表达式Z

((X and Y) or Z)实现同样效果,但要求Y为真

bool函数会把X转换为对应的整数10,实现同样效果,但不执行短路运算

为什么要在意布尔值

X = A or B or C or None

从一个固定大小的集合中选择非空的对象

X = A or default

用来指定一个默认值

if f1() or f2():...

由于短路规则,可能会导致没有调用f2()执行工作


本章习题

  1. 在Python中怎样编写多路分支

    if语句加多个elif分句通常是编写多路分支的最直接的方式

    字典索引运算通常也能实现相同的结果,尤其是字典包含def语句或lambda表达式所写成的可调用函数

  2. 在Python中怎样把if/else语句写成表达式

    表达式Y if X else Z在X为真时会返回Y,否则,返回Z

    and/or组合((X and Y) or Z)也以相同方式工作,但更难懂,而且要求Y为真

  3. 怎样使单个语句横跨多行

    把语句包裹在语法括号()[]{}可以按照需要横跨多行,当Python看见闭合括号时,语句就会结束

    该语句之外的第2行可以以任意缩进层级开始

  4. TrueFalse这两个字代表了什么意义

    TrueFalse是整数10的特殊版本,代表Python中的布尔真假值

    它们可以用来进行真测试、变量初始化以及在交互提示模式中打印表达式结果


第十三章 while和for循环

  • 本章将会遇到两个Python主要循环结构
    • while语句提供编写通用循环的一种方法
    • for语句遍历序列对象内的元素,并对每个元素运行一个代码块
  • 研究一下在循环中不太常用的语句(例如,break和continue)
  • 介绍循环中常用的一些内置函数(例如,range、zip、map和enumerate)

while循环

while语句是Python语言中最通用的迭代结构

只要顶端测试一直计算到真值,就会重复执行一个语句块,当测试变为假时控制其会传给while块后的语句

一般格式

1
2
3
4
while <test>:		# Loop test
<statements1> # Loop body
else: # Optional else
<statements2> # Run if didn't exit loop with break

首行以及测试表达式、有一列或多列缩进语句的主体以及一个可选的else部分

例子

1
2
while True:
print('Type Ctrl-C to stop me')

True总是指向布尔真值,所以Python会一直执行主体,这种行为通常称为无限循环

1
2
3
4
5
6
>>> x = 'spam'
>>> while x: # While x is not empty
... print(x, end=' ')
... x = x[1:] # Strip first character off x
...
spam pam am m

直接测试字符串直到字符串为空返回假时为止

1
2
3
while True:
...loop body...
if exitTest(): break

在循环主体底部以一个测试和break来实现类似其他语言do until功能

break、continue、pass和循环else

  • break

    跳出所在的最近的循环

  • continue

    跳至最近所在循环的开头处

  • pass

    什么也不做,空占位语句

  • 循环else

    只有当循环正常离开时才会执行(没有碰到break语句)

一般循环格式

1
2
3
4
5
6
while <test1>:
<statements1>
if <test2>: break # Exit loop now,skip else
if <test3>: continue # Go to top of loop now,to test1
else:
<statements2> # Run if we didn't hit a 'break'

breakcontinue可以出现在while(或for)循环主体的任何地方,但通常会进一步嵌套在if语句中

pass

pass语句是无运算的占位语句,当语法需要语句而且还没有任何实用的语句可写时,就可以使用它

1
while True: pass		# Type Ctrl-C to stop me!

pass像对象中的None一样,表示什么也没有

pass可以忽略try语句所捕获的异常,以及定义带属性的空类对象

1
2
3
4
5
6
7
8
def func1():
pass # Add real code here later
def func2():
pass
def func1():
... # Alternative to pass
def func1():
... # Does nothing if called

函数体为空会产生语法错误,因为需要使用pass替代

...可以作为pass替代方案,还可以为变量赋值

continue

continue语句会立即跳到循环的顶端,其后的循环代码都不会执行

1
2
3
4
5
6
7
8
9
10
x = 10
while x:
x = x-1 # Or,x -= 1
if x %2 != 0 :continue # Odd?--skip print
print(x, end=' ')
x = 10
while x:
x = x-1
if x %2 == 0: # Even?--print
print(x, end=' ')

因为continue让程序执行时实现跳跃,可读性和可维护性会很差

break

break语句会立刻离开循环,其后的循环代码都不会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> while True:
... name = input('Enter name:')
... if name == 'stop': break
... age = input('Enter age: ')
... print('Hello', name, '=>', int(age) ** 2)
...
Enter name:mel
Enter age: 40
Hello mel => 1600
Enter name:bob
Enter age: 30
Hello bob => 900
Enter name:stop

引入break可以避免嵌套化

循环else

和循环else子句结合时,break语句通常可以忽略其他语言中所需要的搜索状态标志位

1
2
3
4
5
6
7
8
x = y // 2				# For some y > 1
while x > 1:
if y % x == 0: # Remainder
print(y, 'has factor', x)
break # Skip else
x -= 1
else: # Normal exit
print(y, 'is primer')

插入break后,else分句可以视为循环测试恒为假时执行

关于循环else分句的更多内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
found = False
while x and not found:
if match(x[0]): # Value at front?
print('Ni')
found = True
else:
x = x[1:] # Slice off front and repeat
if not found:
print('not found')
while x: # Exit when x empty
if match(x[0]):
print('Ni')
break # Exit,go around else
x = x[1:]
else:
print('Not found') # Only here if exhausted x

else可以替代设定和检查标志位或条件

为什么要在意”模拟C语言的while循环”

C语言编码样式while((x = next()) != NULL) {...process x...}

Python中不允许赋值语句出现在表达式的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while True:
x = next()
if not x: break
...process x...

x = True
while x:
x = next()
if x:
...process x...

x = next()
while x:
...process x...
x = next()

Python中等效的替代方法


for循环

for循环在Python中是一个通用的序列迭代器

for循环可以遍历任何有序的序列对象内的元素,可用于字符串、列表、元组、其他内置可迭代对象以及通过类所
创建的新对象

一般格式

for循环首行定义一个赋值目标,以及想遍历的对象,首行后面是需要重复的语句块

1
2
3
4
5
6
for <target> in <object>:	# Assign object items to target
<statements> # Repeated loop body:use target
if <test>: break # Exit loop now,skip else
if <test>: continue # Go to top of loop now
else:
<statements> # If we didn't hit a 'break'

当Python运行for循环时,会逐个将序列对象中的元素赋值给target,然后为每个元素执行循环主体

for首行中用作赋值目标的变量名通常是for语句所在作用域中的变量,可以在循环主体中修改,但是当控制权再次回到循环
顶端时,就会自动被设成序列中的下一个元素,退出循环后,赋值目标的变量为引用的最后一个对象

for支持else块,如果循环离开时没有碰到break语句时就会执行else

例子

基本应用

1
2
3
4
>>> for x in ["spam", "eggs", "ham"]:
... print(x, end=' ')
...
spam eggs ham

for循环可以遍历任何一种序列对象,把目标变量依次赋值为序列中的每个元素

其他数据类型

1
2
3
4
5
6
7
8
>>> S = "lumberjack"
>>> T = ("and", "I'm", "okay")
>>> for x in S: print(x, end=' ') # Iterate over a string
...
l u m b e r j a c k
>>> for x in T: print(x, end=' ') # Iterate over a tuple
...
and I'm okay

for循环可用于任何序列,甚至不是序列的对象上

在for循环中的元组赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> T = [(1, 2), (3, 4), (5, 6)]
>>> for (a, b) in T: # Tuple assignment at work
... print(a, b)
...
1 2
3 4
5 6
>>> for both in T:
... a, b = both # Manual assignment equivalent
... print(a, b)
...
1 2
3 4
5 6
>>> ((a, b), c) = ((1, 2), 3) # Nested sequences work too
>>> a, b, c
(1, 2, 3)
>>> for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: print(a, b, c)
...
1 2 3
4 5 6

迭代元组序列时,循环目标变量本身可以是一个元组或者是一个嵌套的结构,任何赋值目标变量在语法上都能用在for关键字后

for循环可以自动解包,直接将目标变量进行赋值操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> D = {'a':1, 'b':2, 'c':3}
>>> for key in D:
... print(key, '=>', D[key]) # Use dict keys iterator and index
...
a => 1
b => 2
c => 3
>>> list(D.items())
[('a', 1), ('b', 2), ('c', 3)]
>>> for (key, value) in D.items():
... print(key, '=>', value) # Iterate over both keys and values
...
a => 1
b => 2
c => 3

for循环中的元组变量使得用items方法遍历字典中的键和值变得很方便

Python 3.0在for循环中拓展的序列赋值

1
2
3
4
5
6
7
8
>>> a, *b, c = (1, 2, 3, 4)		# Extended seq assignment
>>> a, b, c
(1, [2, 3], 4)
>>> for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]:
... print(a, b, c)
...
1 [2, 3] 4
5 [6, 7] 8

for循环可以使用Python 3.0的拓展序列解包赋值语法来提取序列中的序列的元素和部分

嵌套for循环

1
2
3
4
5
6
7
8
9
10
11
12
>>> items = ["aaa", 11, (4, 5), 2.01]	# A set of objects
>>> tests = [(4, 5), 3.14] # Keys to search for
>>> for key in tests: # For all keys
... for item in items: # For all items
... if item == key: # Check for match
... print(key, "was found")
... break
... else:
... print(key, "not found!")
...
(4, 5) was found
3.14 not found!

break语句跳出最近的循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> for key in tests:			# For all keys
... if key in items: # Let Python check for a match
... print(key, "was found")
... else:
... print(key, "not found!")
...
(4, 5) was found
3.14 not found!
>>> seq1 = "spam"
>>> seq2 = "scam"
>>> res = []
>>> for x in seq1:
... if x in seq2:
... res.append(x)
...
>>> res
['s', 'a', 'm']

in运算符测试成员关系可以隐性地扫描列表来找到匹配

为什么要在意”文件扫描”

1
2
file = open('test.txt', 'r')		# Read contents into a string
print(file.read())

调用read可以把文件内容一次加载至字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
file = open('test.txt')
while True:
char = file.read(1) # Read by character
if not char: break
print(char)
for char in open('test.txt').read:
print(char)
file = open('test.txt')
while True:
line = file.readline() # Read line by line
if not line: break
print(line, end='') # Line already has a \n
file = open('test.txt', 'rb')
while True:
chunk = file.read(10) # Read byte chunks:up to 10 bytes
if not chunk: break
print
for line in open('test.txt').readlines():
print(line, end='')
for line in open('test.txt'): # Use iterators:best next input mode
print(line, end='')

分块加载文件可以写一个while循环结尾使用break,

使用for循环分块加载文件会一次把文加载至内存

readlines方法会一次把文件载入到一个行字符串的列表


编写循环的技巧

一般forwhile容易写,执行时也比较快

  • 内置range函数返回一系列连续增加的整数,可作为for中的索引
  • 内置zip函数返回并行元素的元组的列表,可用于在for中遍历数个序列

循环计数器:while和range

range函数可用在任何需要整数列表的地方

1
2
>>> list(range(5)), list((range(2, 5))), list(range(0, 10, 2))
([0, 1, 2, 3, 4], [2, 3, 4], [0, 2, 4, 6, 8])
  • 一个参数时,range会产生从零算起的整数列表,但其中不包括该参数的值
  • 两个参数时,第一个将视为下边界
  • 第三个选用参数可以提供步进值,Python会对每个连续整数加上步进值从而得到结果(步进值默认为1)
1
2
3
4
>>> list(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
>>> list(range(5, -5, -1))
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]

range可以是非整数或非递增的整数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> for i in range(3):
... print(i, 'Pythons')
...
0 Pythons
1 Pythons
2 Pythons
>>> X = 'spam'
>>> len(X) # Length of string
4
>>> list(range(len(X))) # All legal offsets into X
[0, 1, 2, 3]
>>> for i in range(len(X)): print(X[i], end=' ') # Manual for indexing
...
s p a m

range函数常用在for循环中,提供了重复特定次数的方法

range函数可以使用for循环进行手动索引列表

非完备遍历:range和分片

1
2
3
4
5
6
7
8
9
>>> S = 'abcdefghijk'
>>> list(range(0, len(S), 2))
[0, 2, 4, 6, 8, 10]
>>> for i in range(0, len(S), 2): print(S[i], end=' ')
...
a c e g i k
>>> for c in S[::2]: print(c, end=' ')
...
a c e g i k

range函数可以做更特殊的遍历种类,例如,遍历过程中跳过一些元素

跳过序列中的元素可以使用限制值形式的分片表达式

使用range不会复制字符串,节省内存

修改列表:range

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> L = [1, 2, 3, 4, 5]
>>> for x in L:
... x += 1
...
>>> L
[1, 2, 3, 4, 5]
>>> x
6
>>> i = 0
>>> while i < len(L):
... L[i] += 1
... i += 1
...
>>> L
[3, 4, 5, 6, 7]
>>> for i in range(len(L)): # Add one to each item in l
... L[i] += 1 # Or L[i] = L[i] + 1
...
>>> L
[2, 3, 4, 5, 6]

无法通过修改遍历列表的循环变量来修改列表,因为修改循环变量引用下一个元素时并不会更新列表

while循环需要多做些工作,且可能运行很慢

可以使用rangefor的组合在遍历列表时对其进行修改,通过range/len组合产生的索引在遍历时将每个位置赋一个值来更新列表

还可以使用列表解析表达式做类似工作,并且不会在原处进行修改

并行遍历:zip和map

内置的zip函数允许使用for循环来并行使用多个序列

在基本运算中,zip会取得一个或多个序列为参数,返回元组的列表,将这些序列中的并排的元素配成对

1
2
3
4
5
6
>>> L1 = [1, 2, 3, 4]
>>> L2 = [5, 6, 7, 8]
>>> zip(L1, L2)
<zip object at 0x10420dec8>
>>> list(zip(L1, L2)) # list() required in 3.0
[(1, 5), (2, 6), (3, 7), (4, 8)]

ziprange一样是一个可迭代对象,需要包含在一个list调用中

1
2
3
4
5
6
7
8
>>> L = [3, 4, 5, 6, 7]
>>> for (x, y) in zip(L1, L2):
... print(x, y, '--', x+y)
...
1 5 -- 6
2 6 -- 8
3 7 -- 10
4 8 -- 12

for循环使用元组赋值运算解包zip结果中的元组实现并行迭代

while循环实现类似的效果需要多做些工作,且可能运行很慢

1
2
3
4
5
6
7
8
9
>>> T1, T2, T3 = (1, 2, 3), (4, 5, 6), (7, 8, 9)
>>> T3
(7, 8, 9)
>>> list(zip(T1, T2, T3))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> list(zip(S1, S2))
[('a', 'x'), ('b', 'y'), ('c', 'z')]

zip函数可以接受任何可迭代的对象,并且可以有两个以上的参数

当参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组

Python 2.6中的map的等价形式

Python 2.X中内置map函数可以用类似方法把序列的元素配对起来

1
2
3
4
>>> S1 = 'abc'
>>> S2 = 'xyz123'
>>> map(None, S1, S2) # 2.X only
[('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None, '3')]

如果参数长度不同,会为较短的序列用None补齐

1
2
>>> list(map(ord, 'spam'))
[115, 112, 97, 109]

一般来讲,map会将一个函数以及一个或多个的序列作为参数,从序列中取出并行元素并传给调用函数,将返回结果收集起来

使用zip构造字典

当键和值的集合必须在运行时计算时,可以用zip函数产生字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
>>> D1 = {}
>>> D1['spam'] = 1
>>> D1['eggs'] = 3
>>> D1['toast'] = 5
>>> list(zip(keys, vals))
[('spam', 1), ('eggs', 3), ('toast', 5)]
>>> D2 = {}
>>> for (k, v) in zip(keys, vals): D2[k] = v
...
>>> D2
{'spam': 1, 'eggs': 3, 'toast': 5}
>>> D3 = dict(zip(keys, vals))
>>> D3
{'spam': 1, 'eggs': 3, 'toast': 5}

可以编写字典常量或者对键进行赋值来创建字典

使用zip函数与for循环可以在运行时获得字典

可以把zip函数返回的key/value列表传给内置的dict函数获得字典

产生偏移和元素:enumerate

enumerate函数可以获得元素以及这个元素的偏移值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
... print(item, 'appears at offset', offset)
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3
>>> E = enumerate(S)
>>> E
<enumerate object at 0x10420fa68>
>>> next(E)
(0, 's')
>>> next(E)
(1, 'p')
>>> next(E)
(2, 'a')

enumerate函数返回一个生成器对象,这个对象有一个__next__方法,由下一个内置函数调用它,并且每次迭代时返回一个(index,value)元组


本章习题

  1. Whilefor之间的主要功能区别是什么

    while循环是一条通用的循环语句

    for循环设计用来在一个序列中遍历各项(序列需要是真正可迭代的)

    尽管while可以用计数器循环来模拟for循环,但它需要更多的代码并且可能运行起来更慢些

  2. breakcontinue之间有何区别

    break语句立即退出一个循环(省略了下面的整个whilefor循环语句)
    continue跳回到循环的顶部(跳转到while中测试之前的部分或for中的下一次元素获取)

  3. 一个循环的else分句何时执行

    whilefor循环中的else分句会在循环离开时执行一次,但前提是循环是正常离开(没有运行break语句),如果有的话,break会立刻离开循环,跳过else部分

  4. 在Python中怎样编写一个基于计数器的循环

    计数器循环可以使用while语句编写,并手动记录索引值

    for循环使用range内置函数来产生连续的整数偏移值

    如果只需要遍历序列中所有元素,任何一种都不是Python中的推荐的做法,只要可能就改用简单的for循环,不用range或计数器,这样做不仅更容易编写,而且通常运行得更快

  5. 怎样使range用于for循环中

    range内置函数可以用在一个for循环中来实现固定次数的重复,以按照偏移值而不是偏移值处的元素来扫描,从而在循环过程中省略连续的元素,并且在遍历一个列表的时候修改它

    这样的用法并非都需要range,大多数有其他的替代方法,例如,扫描实际的元素、三重限制分片以及列表解析往往是较好的解决方案


第十四章 迭代器和解析,第一部分

  • 介绍Python的迭代协议:非序列对象参与迭代循环以及列表解析的方式
  • 介绍列表解析的细节
  • 介绍其他内置迭代工具的使用

迭代器:初探

迭代协议:有__next__方法的对象会前进得到下一个结果,在一系列结果的末尾引发StopIteration异常

任何遵守迭代协议的对象都是可迭代对象,包括实际序列和按照需求而计算的虚拟序列

迭代工具可用于任何可迭代的对象,包括for循环、列表解析、in成员关系测试以及map内置函数等,他们内部工作都是在每次迭代中调用__next__,并且捕捉StopIteration异常来确定合适离开

文件迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> f = open('script1.py')  # Read a  4-line script file in this directory
>>> f.readline() # readline loads one line on each all
'import sys\n'
>>> f.readline()
'print(sys.path)\n'
>>> f.readline()
'x = 2\n'
>>> f.readline()
'print(2 ** 33)\n'
>>> f.readline()
''
>>> f = open('script1.py') # __next__loads one line on each call too
>>> f.__next__() # But raises an exception at end-of-file
'import sys\n'
>>> f.__next__()
'print(sys.path)\n'
>>> f.__next__()
'x = 2\n'
>>> f.__next__()
'print(2 ** 33)\n'
>>> f.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> for line in open('script1.py'):
... print(line.upper(), end='')
...
IMPORT SYS
PRINT(SYS.PATH)
X = 2
PRINT(2 ** 33)

文件对象的readline方法可以一次从一个文件中读取一行文本,每次调用就会前进到下一行,到达文件末尾时返回空字符串

文件对象的__next__方法可以每次调用返回文件中的下一行,到达文件末尾时引发内置的StopIteration异常

for循环在每轮自动调用next从而前进到下一行,这是最简单的写法,运行最快,从内存使用情况来说也是最好的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> for line in open('script1.py').readlines():
... print(line.upper(), end='')
...
IMPORT SYS
PRINT(SYS.PATH)
X = 2
PRINT(2 ** 33)
>>> f = open('script1.py')
>>> while True:
... line = f.readline()
... if not line: break
... print(line.upper(), end='')
...
IMPORT SYS
PRINT(SYS.PATH)
X = 2
PRINT(2 ** 33)

readlines方法将文件内容加载到内存,做成行字符串的列表,如果文件太大,可能导致计算机内存空间不够,甚至不能工作

比起迭代器for循环,while循环逐行读取文件运行得更慢,因为迭代器在Python中是以C语言的速度运行的

手动迭代:iter和next

1
2
3
4
5
6
7
8
9
10
>>> f = open('script1.py')
>>> f.__next__() # Call iteration method directly
'import sys\n'
>>> f.__next__()
'print(sys.path\n'
>>> f = open('script1.py')
>>> next(f) # next built-in calls __next__
'import sys\n'
>>> next(f)
'print(sys.path\n'

内置函数next可以自动调用一个对象的__next__方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> L = [1, 2, 3]
>>> iter(L) is L
False
>>> L.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'
>>> I = iter(L) # Obtain an iterator object
>>> I.__next__() # Call next to advance to next item
1
>>> next(I) # Same as I.__next__()
2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> f = open('script1.py')
>>> iter(f) is f
True
>>> f.__next__()
'import sys\n'

iter内置函数接受一个可迭代的对象,返回含有__next__方法的迭代器

列表与很多其他内置对象支持多次调用迭代器,因此它们自身不是迭代器,需要调用iter函数启动迭代

文件对象就是一个迭代器,拥有__next__方法,不需要传给iter函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> L = [1, 2, 3]
>>> for X in L: # Automatic iteration
... print(X ** 2, end=' ') # Obtains iter,calls __next__,catches exceptions
...
1 4 9
>>> I = iter(L) # Manual iteration:what for loops usually do
>>> while True:
... try: # try statement catches exceptions
... X = next(I) # Or call I.__next__
... except StopIteration:
... break
... print(X ** 2, end=' ')
...
1 4 9

迭代工具会将可迭代对象传给iter内置函数获得一个含有__next__方法的迭代器

可以手动地应用迭代协议,可以使用__next__方法或者next函数

try语句运行一个动作并且捕获在运行过程中发生的异常

其他内置类型迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> for key in D.keys():
... print(key, D[key])
...
a 1
b 2
c 3
>>> I = iter(D)
>>> next(I)
'a'
>>> next(I)
'b'
>>> next(I)
'c'
>>> next(I)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> for key in D:
... print(key,D[key])
...
a 1
b 2
c 3

遍历字典键可以明确地获取键的列表

字典有一个迭代器,在迭代环境中可以自动一次返回一个键

1
2
3
4
5
6
7
8
9
P = os.popen('cd')
>>> import os
>>> P = os.popen('ls')
>>> P.__next__()
'test.py\n'
>>> next(P)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

shelves(用于Python对象的一个根据键访问的文件系统)和os.popen(读取shell命令输出的一个工具)的结果也是可迭代的

popen对象支持P.next()方法,但不支持next(P)内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> R = range(5)
>>> R # Ranges are iterables in 3.0
range(0, 5)
>>> I = iter(R) # Use iteration protocol to produce results
>>> next(I)
0
>>> next(I)
1
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> E = enumerate('spam') # enumerate is an iterable too
>>> E
<enumerate object at 0x104216c60>
>>> I = iter(E)
>>> next(I) # Generate results with iteration protocol
(0, 's')
>>> next(I) # Or use list to force generation to run
(1, 'p')
>>> list(enumerate('spam'))
[(0, 's'), (1, 'p'), (2, 'a'), (3, 'm')]

由于迭代协议一次返回一个结果,只能把某些结果包装到list调用以一次性得到完整的值


列表解析:初探

列表解析是最常应用迭代协议的环境之一

1
2
3
4
5
6
7
8
9
>>> L = [1, 2, 3, 4, 5]
>>> for i in range(len(L)):
... L[i] += 10
...
>>> L
[11, 12, 13, 14, 15]
>>> L = [x + 10 for x in L]
>>> L
[21, 22, 23, 24, 25]

列表解析可以用单个表达式替代循环产生所需的结果列表,代码较少且可能运行的更快

列表解析会产生一个新的列表对象

列表解析基础知识

列表解析写在一个方括号中,以一个任意的表达式开始,后边跟着for循环头部,它声明了循环变量和一个可迭代对象

Python在解释器内部执行一个迭代,按照顺序把循环变量赋值为每个元素引用,得到一个对循环变量运行左边表达式的结果

可以用for循环手动地构建一个表达式结果的列表,但是列表解析编写更加精简,可用于多种环境,且比for循环语句运行的更快

在文件上使用列表解析

1
2
3
4
5
6
7
8
9
10
>>> f = open('script1.py')
>>> lines = f.readlines()
>>> lines
['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n']
>>> lines = [line.rstrip() for line in lines]
>>> lines
['import sys', 'print(sys.path', ')x = 2', 'print(2 ** 33)']
>>> lines = [line.rstrip() for line in open('script1.py')]
>>> lines
['import sys', 'print(sys.path', ')x = 2', 'print(2 ** 33)']

文件对象的readlines方法一次性把文件载入到包含所有行字符串的列表

通过列表解析对每一行运行rstrip方法可以移除右端的空白(当确保所有的行正确结束时可以使用分片)

列表解析与for循环一样是一个迭代环境,在表达式中打开文件时,列表解析会自动使用迭代协议调用文件的next方法

在表达式中打开文件时,Python会扫描文件并构建操作结果的一个列表

1
2
3
4
5
6
7
8
9
10
>>> [line.upper() for line in open('script1.py')]
['IMPORT SYS\n', 'PRINT(SYS.PATH)\n', 'X = 2\n', 'PRINT(2 ** 33)\n']
>>> [line.rstrip().upper() for line in open('script1.py')]
['IMPORT SYS', 'PRINT(SYS.PATH)', 'X = 2', 'PRINT(2 ** 33)']
>>> [line.split() for line in open('script1.py')]
[['import', 'sys'], ['print(sys.path)'], ['x', '=', '2'], ['print(2', '**', '33)']]
>>> [line.replace(' ', '!') for line in open('script1.py')]
['import!sys\n', 'print(sys.path)\n', 'x!=!2\n', 'print(2!**!33)\n']
>>> [('sys' in line,line[0]) for line in open('script1.py')]
[(True, 'i'), (True, 'p'), (False, 'x'), (False, 'p')]

可以在迭代时在一个文件的行上运行任何的字符串操作

拓展的列表解析语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> lines = [line.rstrip() for line in open('script1.py') if line[0] == 'p']
>>> lines
['print(sys.path)', 'print(2 ** 33)']
>>> res = []
>>> for line in open('script1.py'):
... if line[0] == 'p':
... res.append(line.rstrip())
...
>>> res
['print(sys.path)', 'print(2 ** 33)']
>>> [x + y for x in 'abc' for y in 'lmn']
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
>>> res = []
>>> for x in 'abc':
... for y in 'lmn':
... res.append(x + y)
...
>>> res
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']

列表解析表达式中嵌套的for循环可以有一个相关的if子句来过滤测试不为真的结果项

通常可以把列表解析转换为一条for语句,但可能运行起来要慢很多

列表解析可以包含嵌套的循环,也编写一系列的for子句,每个子句有一个可选的if子句


其他迭代环境

实现了迭代协议的任何工具,都能够在提供了该工具的任何内置类型或用户定义的类上自动地工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> for line in open('script1.py'):     # Use file iterators
... print(line.upper(), end='')
...
IMPORT SYS
PRINT(SYS.PATH)
X = 2
PRINT(2 ** 33)
>>> uppers = [line.upper() for line in open('script1.py')]
>>> uppers
['IMPORT SYS\n', 'PRINT(SYS.PATH\n', ')X = 2\n', 'PRINT(2 ** 33)\n']
>>> map(str.upper, open('script1.py'))
<map object at 0x104218828>
>>> list(map(str.upper, open('script1.py')))
['IMPORT SYS\n', 'PRINT(SYS.PATH\n', ')X = 2\n', 'PRINT(2 ** 33)\n']
>>> 'y = 2\n' in open('script1.py')
False
>>> 'x = 2\n' in open('script1.py')
True

for循环、列表解析、in成员关系测试、map内置函数都使用了迭代协议

所有使用文件对象的迭代器都自动地按行扫描

map函数把一个函数应用于传入的可迭代对象中的每一项,返回一个可迭代的对象,需要包含到list调用中迫使其一次性给出所有的值

1
2
3
4
5
6
7
8
9
10
11
>>> sorted(open('script1.py'))
['import sys\n', 'print(2 ** 33)\n', 'print(sys.path)\n', 'x = 2\n']
>>> list(zip(open('script1.py'), open('script1.py')))
[('import sys\n', 'import sys\n'), ('print(sys.path)\n', 'print(sys.path)\n'), ('x = 2\n', 'x = 2\n'), ('print(2 ** 33)\n', 'print(2 ** 33)\n')]
>>> list(enumerate(open('script1.py')))
[(0, 'import sys\n'), (1, 'print(sys.path)\n'), (2, 'x = 2\n'), (3, 'print(2 ** 33)\n')]
>>> list(filter(bool,open('script1.py')))
['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n']
>>> import functools,operator
>>> functools.reduce(operator.add, open('script1.py'))
'import sys\nprint(sys.path)\nx = 2\nprint(2 ** 33)\n'

sorted函数接受任何可迭代的对象并返回一个新的排序的列表作为结果

zip组合多个可迭代对象中的各项

enumerate返回可迭代对象中的偏移与项

filter选择一个函数返回为真的项

reduce针对可迭代对象中的成对的项运行一个函数

这些函数都接受一个可迭代对象,zipenumeratefilter返回一个可迭代对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> sum([3, 2, 4, 1, 5, 0]) # sum expects numbers only
15
>>> any(['spam', '', 'ni'])
True
>>> all(['spam', '', 'ni'])
False
>>> max([3, 2, 5, 1, 4])
5
>>> min([3, 2, 5, 1, 5])
1
>>> max(open('script1.py')) # Line with max/min string value
'x = 2\n'
>>> min(open('script1.py'))
'import sys\n'

sum函数计算任何可迭代对象中的总数

1
2
any`函数在一个可迭代对象中任何的项为真时返回`True
all`函数在一个可迭代对象中所有的项为真时返回`True

maxmin函数分别返回一个可迭代对象中的最大和最小的项,当对象为文件时返回具有最高的和最低的字符串值的行

他们都接受任何一个可迭代的对象作为一个参数,并且使用迭代协议来扫描它,但返回单个对象

1
2
3
4
5
6
7
8
9
10
11
12
>>> list(open('script1.py'))
['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n']
>>> tuple(open('script1.py'))
('import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n')
>>> '&&'.join(open('script1.py'))
'import sys\n&&print(sys.path)\n&&x = 2\n&&print(2 ** 33)\n'
>>> a, b, c, d = open('script1.py')
>>> a, d
('import sys\n', 'print(2 ** 33)\n')
>>> a, *b = open('script1.py') # 3.0 extended form
>>> a, b
('import sys\n', ['print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n'])

所有从左到右地扫描一个传入的对象的工具都使用了迭代协议

listtuple内置函数从可迭代对象构建了一个新的对象

dict函数接收一个可迭代的zip结果

str.join方法将一个子字符串放置到一个可迭代对象中所有的字符串项之间

序列赋值从左到右扫描对象进行赋值

1
2
3
4
5
6
7
8
9
10
>>> set(open('script1.py'))
{'print(2 ** 33)\n', 'import sys\n', 'print(sys.path)\n', 'x = 2\n'}
>>> {line for line in open('script1.py')}
{'print(2 ** 33)\n', 'import sys\n', 'print(sys.path)\n', 'x = 2\n'}
>>> {ix: line for ix, line in enumerate(open('script1.py'))}
{0: 'import sys\n', 1: 'print(sys.path)\n', 2: 'x = 2\n', 3: 'print(2 ** 33)\n'}
>>> {line for line in open('script1.py') if line[0] == 'p'}
{'print(2 ** 33)\n', 'print(sys.path)\n'}
>>> {ix: line for ix, line in enumerate(open('script1.py')) if line[0] == 'p'}
{1: 'print(sys.path)\n', 3: 'print(2 ** 33)\n'}

集合解析和字典解析与列表解析一样,都支持列表解析的拓展语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> def f(a, b, c, d): print(a, b, c, d, sep='&')
...
>>> f(1, 2, 3, 4)
1&2&3&4
>>> f(*[1, 2, 3, 4]) # Unpackeds into arguments
1&2&3&4
>>> f(*open('script1.py')) # Iterates by lines too!
import sys
&print(sys.path)
&x = 2
&print(2 ** 33)

>>> X = (1, 2)
>>> Y = (3, 4)
>>> list(zip(X, Y)) # Zip tiples:returns an iterable
[(1, 3), (2, 4)]
>>> A, B = zip(*zip(X, Y)) # Unzip a zip!
>>> A
(1, 2)
>>> B
(3, 4)

函数调用中参数的特殊形式*arg接收任何可迭代对象,把一个集合的值解包为单个的参数

*将一个可迭代对象解包,使用zip函数可以重新组合


Python 3.0中的新的可迭代对象

字典方法keysvaluesitems与内置函数rangemapzipfilter都处理可迭代对象

1
2
3
4
>>> zip('abc', 'xyz')           # An iterable in Python 3.0
<zip object at 0x104717e88>
>>> list(zip('abc', 'xyz')) # Force list of results in 3.0 to display
[('a', 'x'), ('b', 'y'), ('c', 'z')]

在Python 3.0中,这些函数返回可迭代的对象,根据需要产生结果,可以节约内存,但是显示结果需要额外的录入

range迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> R = range(10)       # range returns an iterator,not a list 
>>> R
range(0, 10)
>>> I = iter(R) # Make an iterator from the range
>>> next(I) # Advance to next result
0 # What happens in for loops:conprehensions,etc
>>> next(I)
1
>>> next(I)
2
>>> list(range(10)) # To force a listof
range(0, 10)
>>> len(R) # range also does len and indexing,but no others
10
>>> R[0]
0
>>> R[-1]
9
>>> next(I) # Continue taking from interator,where left off
3
>>> I.__next__()
4

range内置函数返回一个迭代器,根据需要产生范围的数字

range对象只支持迭代、索引和len函数

使用list(range(...))强制产生一个真正的范围列表

map、zip和filter迭代器

mapzip以及filiter内置函数步进处理可迭代对象,而且在Python 3.0中返回一个迭代器而不再在内存中一次性生成一个结果列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
>>> M = map(abs, (-1, 0, 1))        # map returns an iterator,not a list
>>> M
<map object at 0x10421e0f0>
>>> next(M) # Use iterator manually:exhausts results
1 # These do not support len() or indexing
>>> next(M)
0
>>> next(M)
1
>>> next(M)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> for x in M: print(x) # map iterator is now empty:ont pass only
...
>>> M = map(abs, (-1, 0, 1)) # Make a new iterator to scan again
>>> for x in M: print(x) # Iteration contexts auto call next()
...
1
0
1
>>> list(map(abs, (-1, 0, 1))) # Can force a real list if needed
[1, 0, 1]
>>> Z = zip((1, 2, 3),(10, 20, 30)) # zip is the same:a one-pass iterator
>>> Z
<zip object at 0x10421a508>
>>> list(Z)
[(1, 10), (2, 20), (3, 30)]
>>> for pair in Z: print(pair) # Exhausted after one pass
...
>>> Z = zip((1, 2, 3),(10, 20, 30))
>>> for pair in Z: print(pair) # Iterator used automatically or manually
...
(1, 10)
(2, 20)
(3, 30)
>>> Z = zip((1, 2, 3),(10, 20, 30))
>>> next(Z)
(1, 10)
>>> next(Z)
(2, 20)
>>> filter(bool, ['spam', '', 'ni'])
<filter object at 0x10474ff98>
>>> list(filter(bool, ['spam', '', 'ni']))
['spam', 'ni']

range需要手动使用iter产生一个迭代器不同,这些函数都是自己的迭代器,在遍历其结果一遍后就用尽

多个迭代器 VS 单个迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
>>> R = range(3)                    # range allows multiple iterators
>>> next(R)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'range' object is not an iterator
>>> I1 = iter(R)
>>> next(I1)
0
>>> next(I1)
1
>>> I2 = iter(R) # Two iterators on one range
>>> next(I2)
0
>>> next(I2)
1
>>> next(I1) # I1 is at a different spot than 12
2
>>> Z = zip((1, 2, 3), (10, 11, 12))
>>> I1 = iter(Z)
>>> I2 = iter(Z)
>>> next(I1)
(1, 10)
>>> next(I1)
(2, 11)
>>> next(I2) # I2 is same spot as I1
(3, 12)
>>> M = map(abs, (-1, 0, 1)) # Ditto for map(and filter)
>>> I1 = iter(M); I2 = iter(M)
>>> print(next(I1), next(I1), next(I1))
1 0 1
>>> next(I2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> R = range(3) # But range allows many iterators
>>> I1, I2 = iter(R), iter(R)
>>> [next(I1), next(I1), next(I1)]
[0 1 2]
>>> next(I2)
0

range支持len和索引,它不是自己的迭代器(手动迭代时,使用iter产生一个迭代器),并且支持将其结果作为多个迭代器

zipmapfilter则不支持相同结果上的多个活跃迭代器

字典视图迭代器

在Python 3.0中,字典的keysvaluesitems方法返回可迭代的视图对象,它们一次产生一个结果项,而不是在内存中一次产生全部结果列表

视图项保持和字典中的那些项相同的物理顺序,并且可以对底层的字典做出的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
>>> D = dict(a=1, b=2, c=3)
>>> D
{'a': 1, 'b': 2, 'c': 3}
>>> K = D.keys() # A view object in 3.0,not a list
>>> K
dict_keys(['a', 'b', 'c'])
>>> next(K) # Views are not iterators themselves
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'dict_keys' object is not an iterator
>>> I = iter(K) # Views have an iterator.
>>> next(I) # which can be used manually
'a' # but does not support len(),index
>>> next(I)
'b'
>>> for k in D.keys(): print(k, end=' ') # All iteration contexts use auto
...
a b c
>>> K = D.keys()
>>> list(K) # Can still force a real list if needed
['a', 'b', 'c']
>>> V = D.values() # Ditto for values() and items() views
>>> V
dict_values([1, 2, 3])
>>> list(V)
[1, 2, 3]
>>> list(D.items())
[('a', 1), ('b', 2), ('c', 3)]
>>> for (k,v) in D.items(): print(k, v, end=' ')
...
a 1 b 2 c 3
>>> I = iter(D) # Dictionaries still have own iterator
>>> next(I) # Returns next key on each iteration
'a'
>>> next(I)
'b'
>>> for key in D: print(key, end=' ') # Still no need to call keys() to iterate
... # But keys is an iterator in 3.0 too!
a b c

字典视图可以传递到list函数中,强制构建一个真正的列表

字典有自己的迭代器,可以返回连续的键

1
2
3
4
5
6
7
8
9
>>> D = {'a': 1, 'b': 2, 'c': 3}
>>> for k in sorted(D.keys()): print(k, D[k], end=' ')
...
a 1 b 2 c 3
>>> D
{'a': 1, 'b': 2, 'c': 3}
>>> for k in sorted(D):print(k, D[k], end=' ')
...
a 1 b 2 c 3

无法按照排序的键返回一个字典,可以用一个list调用来转换keys视图,或者在一个键视图或字典自身上使用sorted调用


其他迭代器主题

  • 使用yield语句,用户定义的函数可以转换为可迭代的生成器函数
  • 当编写在圆括号中的时候,列表解析转变为可迭代的生成器表达式
  • 用户定义的类通过__iter____getitem__运算符重载变得可迭代

本章习题

  1. for循环和迭代器之间有什么关系

    for循环会使用迭代协议来遍历迭代对象中的每一个项

    for循环会在每次迭代中调用该对象的__next__方法(由next内置函数运行),而且会捕捉StopIteration 异常,从而决定何时停止循环

    支持这种模式的任何对象,都可以用于for循环以及其他迭代环境中

  2. for循环和列表解析直接有什么关系

    两者都是迭代工具

    列表解析是执行常见for循环任务的简明并且高效的方法:对可迭代对象内所有元素应用一个表达式,并收集其结果

    可以把列表解析转换成for循环,而列表解析表达式的一部分的语法看起来就像是for循环的首行

  3. 举出Python中的4种迭代环境

    Python中的迭代环境包括for循环、列表解析、map内置函数、in成员关系测试表达式以及内置函数sortedsumanyall

    这个分类也包括了内置函数listtuple、字符串join方法以及序列赋值运算

    所有这些都使用了迭代协议(next方法)来一次一个元素逐个遍历可迭代对象

  4. 如今从一个文本文件逐行读取行的最好的方法是什么

    如今从文本文件中读取文本行的最佳方式是不要刻意去读取:其替代方法是,在迭代环境中打开文件,诸如for循环或列表解析中,然后,让迭代工具在每次迭代中执行该文件的next方法,自动一次扫描一行

    从代码编写的简易性、执行速度以及内存空间需求等方面来看,这种做法通常都是最佳方式


第十五章 文档

  • 本章介绍程序的文档概念
  • 认识了文档字符串
  • 探索Python的在线手册
  • 学习PyDoc的help函数和网页接口如何提供额外的文档来源
  • 复习常见的编写代码的错误

Python文档资源

Python预置的功能数量惊人:内置函数和异常、预先定义的对象属性和方法、标准库模块

Python文档资源

形式 角色
#注释 文件中的文档
dir函数 对象中可用属性的列表
文档字符串:_doc_ 附加在对象上的文件中的文档
PyDoc:help函数 对象的交互帮助
PyDoc:HTML报表 浏览器中的模块文档
标准手册 正式的语言和库的说明
网站资源 在线教程、例子等
出版的书籍 商业参考书籍

#注释

#注释是代码编写文档的最基本方式

Python会忽略#之后所有文字(只要#不是位于字符串常量中),可以在#插入一些对程序员有意义的文字和说

不过这类注释只能从源代码文件中看到

dir函数

内置的dir函数是抓取对象内可用所有属性列表的简单方式,它能够调用任何有属性的对象

1
2
3
4
5
6
7
8
9
10
11
>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'set_asyncgen_hooks', 'set_coroutine_wrapper', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions']
>>> dir([])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> dir('')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> dir(str) == dir('') # Same result as prior example
True
>>> dir(list) == dir([])
True

可以显示模块的诸多变量名

可以为dir传入所需类型的常量类型的名称找出内置对象提供的属性,任何内置类型的dir结果都包含了一组属性,这些属性与该类型的实现相关

文档字符串:_doc_

Python支持自动附加在对象上的文档,运行时可以查看保存

文档字符串是写成字符串,放在模块文件、函数以及类语句的顶端的注释

Python自动封装文档字符串,使其成为相应对象的__doc__属性

用户定义的文档字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""
Module documentation
Words Go Here
"""

spam = 40

def square(x):
"""
function documentation
can we have your liver then?
"""
return x ** 2 # square

class Employee:
"class documentation"
pass

print(square(4))
print(square.__doc__)

文档字符串出现在文件开端以及其中的函数和类的开头

文档协议的重点在于注释会保存在__doc__属性中以供查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> import docstrings
16

function documentation
can we have your liver then?

>>> print(docstrings.__doc__)

Module documentation
Words Go Here

>>> print(docstrings.square.__doc__)

function documentation
can we have your liver then?

>>> print(docstringstest4.Employee.__doc__)
class documentation

导入文件并打印其__doc__属性可以显示模块以及对象关联的文档字符串

可以通过路径访问类module.class.method.__doc__

文档字符串标准

文档字符串的结构没有标准

现在有各种标记语言和末班协议(HTML或XML)

内置文档字符串

Python内置模块和对象都可以查看可读的说明信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
>>> import sys
>>> print(sys.__doc__)
This module provides access to some objects used or maintained by the
interpreter and to functions that interact strongly with the interpreter.

Dynamic objects:

argv -- command line arguments; argv[0] is the script pathname if known
path -- module search path; path[0] is the script directory, else ''
modules -- dictionary of loaded modules

displayhook -- called to show results in an interactive session
excepthook -- called to handle any uncaught exception other than SystemExit
To customize printing in an interactive session or to install a custom
top-level exception handler, assign other functions to replace these.

stdin -- standard input file object; used by input()
stdout -- standard output file object; used by print()
stderr -- standard error object; used for error messages
By assigning other file objects (or objects that behave like files)
to these, it is possible to redirect all of the interpreter's I/O.

last_type -- type of last uncaught exception
last_value -- value of last uncaught exception
last_traceback -- traceback of last uncaught exception
These three are only available in an interactive session after a
traceback has been printed.

Static objects:

builtin_module_names -- tuple of module names built into this interpreter
copyright -- copyright notice pertaining to this interpreter
exec_prefix -- prefix used to find the machine-specific Python library
executable -- absolute path of the executable binary of the Python interpreter
float_info -- a struct sequence with information about the float implementation.
float_repr_style -- string indicating the style of repr() output for floats
hash_info -- a struct sequence with information about the hash algorithm.
hexversion -- version information encoded as a single integer
implementation -- Python implementation information.
int_info -- a struct sequence with information about the int implementation.
maxsize -- the largest supported length of containers.
maxunicode -- the value of the largest Unicode code point
platform -- platform identifier
prefix -- prefix used to find the Python library
thread_info -- a struct sequence with information about the thread implementation.
version -- the version of this interpreter as a string
version_info -- version information as a named tuple
__stdin__ -- the original stdin; don't touch!
__stdout__ -- the original stdout; don't touch!
__stderr__ -- the original stderr; don't touch!
__displayhook__ -- the original displayhook; don't touch!
__excepthook__ -- the original excepthook; don't touch!

Functions:

displayhook() -- print an object to the screen, and save it in builtins._
excepthook() -- print an exception and its traceback to sys.stderr
exc_info() -- return thread-safe information about the current exception
exit() -- exit the interpreter by raising SystemExit
getdlopenflags() -- returns flags to be used for dlopen() calls
getprofile() -- get the global profiling function
getrefcount() -- return the reference count for an object (plus one :-)
getrecursionlimit() -- return the max recursion depth for the interpreter
getsizeof() -- return the size of an object in bytes
gettrace() -- get the global debug tracing function
setcheckinterval() -- control how often the interpreter checks for events
setdlopenflags() -- set the flags to be used for dlopen() calls
setprofile() -- set the global profiling function
setrecursionlimit() -- set the max recursion depth for the interpreter
settrace() -- set the global debug tracing function
>>> print(sys.getrefcount.__doc__)
getrefcount(object) -> integer

Return the reference count of object. The count returned is generally
one higher than you might expect, because it includes the (temporary)
reference as an argument to getrefcount().
>>> print(int.__doc__)
int(x=0) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given. If x is a number, return x.__int__(). For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base. The literal can be preceded by '+' or '-' and be surrounded
by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
>>> print(map.__doc__)
map(func, *iterables) --> map object

Make an iterator that computes the function using arguments from
each of the iterables. Stops when the shortest iterable is exhausted.

查看内置模块的可读说明,可将其导入,并打印其__doc__字符串

内置模块内的函数、类以及方法在其__doc__属性内也有附加的说明信息

也可以通过文档字符串读取内置函数的说明

PyDoc:help函数

PyDoc工具是Python程序代码,知道如何提取文档字符串并且自动提取其结构化的信息,并将其格式化成各种类型的排列友好的报表

Python在其标准库中附带了PyDoc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> import sys
>>> help(sys.getrefcount)

Help on built-in function getrefcount in module sys:

getrefcount(...)
getrefcount(object) -> integer

Return the reference count of object. The count returned is generally
one higher than you might expect, because it includes the (temporary)
reference as an argument to getrefcount().
>>> help(sys)

Help on built-in module sys:

NAME
sys

MODULE REFERENCE
https://docs.python.org/3.6/library/sys

The following documentation is automatically generated from the Python
source files. It may be incomplete, incorrect or include features that
are considered implementation detail and may vary between Python
implementations. When in doubt, consult the module reference at the
location listed above.

help函数会启用PyDoc从而产生简单的文字报表

当对象较大时,如模块和类,help显示内容会分成几段,有些是文档字符串,有些是PyDoc自动查看对象内部而收集的结构化信息,通过交互模式查看所有段内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
>>> help(dict)

Help on class dict in module builtins:

class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object's
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
| dict(**kwargs) -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)
>>> help(object)

Help on class object in module builtins:

class object
| The most base type
(END)
>>> help(str.replace)

Help on method_descriptor:

replace(...)
S.replace(old, new[, count]) -> str

Return a copy of S with all occurrences of substring
old replaced by new. If the optional argument count is
given, only the first count occurrences are replaced.
(END)
>>> help(ord)

Help on built-in function ord in module builtins:

ord(c, /)
Return the Unicode code point for a one-character string.
(END)

可以对内置函数、方法以及类型使用help,取得内置类型的help信息可以使用类型名称(dictstrlist)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
>>> import docstrings
16

function documentation
can we have your liver then?

>>> help(docstrings.square)

Help on function square in module docstrings:

square(x)
function documentation
can we have your liver then?
(END)
>>> help(docstrings.Employee)

Help on class Employee in module docstrings:

class Employee(builtins.object)
| class documentation
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
(END)
>>> help(docstrings)

Help on module docstrings:

NAME
docstrings

DESCRIPTION
Module documentation
Words Go Here

CLASSES
builtins.object
Employee

class Employee(builtins.object)
| class documentation
|
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

FUNCTIONS
square(x)
function documentation
can we have your liver then?

DATA
spam = 40

FILE
~/Desktop/test/docstrings.py

(END)

help可以用在模块上,其中有些是文档字符串,有些是查看对象的结构而自动取出的信息

PyDoc:HTML函数

PyDoc提供了GUI接口,可以将其报表通过HTML网页格式来呈现,PyDoc可以在本地运行或者作为客户端/服务器模式中的远程服务器来运行

GUI模式PyDoc的启动

  • 首先通过Windows PythonStart按钮中的Module Docs菜单,或者PythonTools目录下的pydocgui.pyw脚本,或者执行pydoc. py再带一个-g命令行参数来启动搜索引擎GUI
  • 输入模块名称,按下回车键,Python会深人到模块的导入搜索路径(sys.path)从而寻找所请求的模块的索引内容
  • 找到对象后,选中,点击go to selected,Python会在机器上打开网页浏览器,以HTML格式显示报表

网页Module部分超链接可以跳到相关模块的PyDoc网页,当网页较大时,也会产生超链接指向网页的不同部分

GUI接口也能用在用户定义的模块上

PyDoc技巧

当搜索模块名称留空时,按下go to selected按钮,PyDoc会产生一个网页,其中包含了可能在计算机上导人的每个模块的超链接,这包括Python标准库模块、已安装的第三方扩展模块、位于导入搜索路径上的用户定义模块以及静态或动态连结的C程序模块

PyDoc可以把模块的HTML文档保存在文件中,以便在今后查看或打印

由于以GUII模式执行时可能和标准输入文字没有连接,PyDoc可能无法很好的运行对象是从标准输入读取数据的脚本

标准手册集

Python手册以HTML和其他格式来实现

可以在Windows上从开始按钮的Python选单中选取,或者在IDLE的Help选项菜单中开启,或者从http://www.python.org获得不同格式的手册,或者在该网站上在线阅读,Windows上的手册与在线版本支持搜索

在Windows格式的手册上,Library Reference说明内置类型、函数、异常以及标准库模块,Language Regerence提供语言层次的细节的官方说明

网络资源

在官方的Python程序设计语言网站上有各种Python资源的链接,而其中一些涵盖了特定的主题或领域

点击Documentation链接可以获取在线教程以及Beginners Guide to Python,这个网站也列出了非英文的Python资源

Web上有各种Python维基、博客、网站以及其他许多资源

已出版的书籍

书籍会比Python最新的变动慢得多


常见编写代码的陷阱

  • 冒号

    复合语句首行末尾输入:(if、while、for等的第一行)

  • 从第1行开始

    要确定顶层(无嵌套)程序代码从第1列开始,包括在模块文件中输入的无嵌套的代码,在交互模式提示符下输人的无嵌套的代码

  • 空白行在交互模式提示符下很重要

    模块文件中复合语句内的空白行都会被Python解释器忽视,但是,交互模式提示符下输入代码时,空白行则是结束语句,如果想继续,就不要在...提示符下( 或IDLE中)按Enter键,直到完成为止

  • 不要在Python中写C代码

    • ifwhile首行,不用在测试两侧输入括号(例如,if(X==1):),虽然可以这么做.但这是多余的
    • 不要以分号终止所有的语句,这么做在技术上也是合法的,但是完全没用,除非把一个以上的语句放在同一行中(每行的结尾通常就是该语句的终结)
    • 不要在while循环测试中嵌人赋值语句
    • 不要在块周围使用{}
  • 使用简单的for循环,而不是whilerange

    比起while或者range式的计数器循环来讲,简单的for循环(例如,for x in seq:) 总是比较容易写,运行起来也更快

    Python会在内部为简单的for循环处理索引运算,因此有时会比等效的while快两倍

  • 要注意赋值语句中的可变对象

    在多重目标赋值语句(a = b=[])以及在增强指定语句(a += [1,2])中使用可变对象时,在原处的修改会影响其他变量

  • 不要期待在原处修改对象的函数会返回结果

    list.appendlist.sort方法,并不会有返回值(除None),所以在调用时不要对其结果进行赋值

    Python 2.X的代码中for k in D.keys().sort():keys方法建立键列表,sort方法排序,但是由于sort方法返回None会导致循环失败

  • 一定要使用括号调用函数

    必须在函数名称后面加括号才能对它进行调用,无论它是否带有参数(例如,使用function())

    函数是对象,只是有特殊的运算()触发对它的调用
    输入file.close因为是引用函数而不是对它调用所以也是合法的,但是它并没有关闭文件

  • 不要在导入和重载中使用扩展名或路径

    import语句中省略目录路径和文件拓展名(例如,要写import mod,而不是import mod.py),因为模块可能有.py以外的其他后缀名(例如,.pyc),硬编码的后缀名不仅是不合法的语法,也说不通

    任何平台特定的目录路径语法是属于模块搜索路径设置的,而不是import语句


本章习题

  1. 在什么时候应该使用文档字符串而不是#字注释

    文档字符串被认为最适用于较大、功能性的文档,用来描述程序中的模块、函数、类以及方法的使用

    如今的#号注释最好只限于关于费解的表达式或语句的微型文档

    一方面因为文件字符串在源代码文件中比较容易找到,另一方面也是因为PyDoc系统能将其取出并显示

  2. 举出3种查看文档字符串的方式

    可以打印对象的__ doc__ 属性,传给PyDochelp函数,以及选取服务器/客户端模式下PyDoc GUI搜索引擎中的模块,查看文档字符串

    此外,PyDoc可以把模块的文档储存在HTML文件中以便稍后查看或打印

  3. 如何获得对象中可用属性的列表

    内置的dir(X)函数会返回附加在任何对象上的所有属性的列表

  4. 如何获得计算机中所有可用模块的列表

    执行PyDoc GUI接口,保持模块名称空白,然后选择Open Browser,这样会打开一个网页,其中包含了程序中每个可用模块的链接

  5. 阅读本书之后,应该买哪本Python书籍

    前言列出的一些推荐的进阶书籍,包括了参考书籍和应用程序开发的教程

CATALOG
  1. 1. 第十章 Python语句简介
    1. 1.1. 重访Python程序结构
      1. 1.1.1. Python的语句
    2. 1.2. 两个if的故事
      1. 1.2.1. Python增加了什么
      2. 1.2.2. Python删除了什么
        1. 1.2.2.1. 括号是可选的
        2. 1.2.2.2. 终止行就是终止语句
        3. 1.2.2.3. 缩进的结束就是代码块的结束
      3. 1.2.3. 为什么使用缩进语法
      4. 1.2.4. 几个特殊实例
        1. 1.2.4.1. 语句规则的特殊情况
        2. 1.2.4.2. 代码块规则特殊实例
    3. 1.3. 简短实例:交互循环
      1. 1.3.1. 一个简单的交互式循环
      2. 1.3.2. 对用户输入数据做数学运算
      3. 1.3.3. 用测试输入数据来处理错误
      4. 1.3.4. 用try语句处理错误
      5. 1.3.5. 嵌套代码三层
    4. 1.4. 本章习题
  2. 2. 第十一章 赋值、表达式和打印
    1. 2.1. 赋值语句
      1. 2.1.1. 赋值语句的形式
      2. 2.1.2. 序列赋值
        1. 2.1.2.1. 高级序列赋值语句模式
      3. 2.1.3. Python 3.0中的拓展序列解包
        1. 2.1.3.1. 扩展的解包的实际应用
        2. 2.1.3.2. 边界情况
        3. 2.1.3.3. 一个有用的便利形式
        4. 2.1.3.4. 应用于for循环
      4. 2.1.4. 多目标赋值语句
        1. 2.1.4.1. 多目标赋值以及共享引用
      5. 2.1.5. 增强赋值语句
        1. 2.1.5.1. 增强赋值以及共享引用
      6. 2.1.6. 变量命名规则
        1. 2.1.6.1. 基本规则
        2. 2.1.6.2. Python 3.0中的保留字
        3. 2.1.6.3. 命名规则
        4. 2.1.6.4. 变量名没有类型,但对象有
      7. 2.1.7. Python的废弃协议
    2. 2.2. 表达式语句
      1. 2.2.1. 表达式语句和在原处的修改
    3. 2.3. 打印操作
      1. 2.3.1. Python 3.0的print函数
        1. 2.3.1.1. 调用格式
        2. 2.3.1.2. Python 3.0的print函数的应用
      2. 2.3.2. Python 2.6print语句
        1. 2.3.2.1. 语句形式
        2. 2.3.2.2. Python 2.6 print语句应用
      3. 2.3.3. 打印流重定向
        1. 2.3.3.1. Python的”Hello World”程序
        2. 2.3.3.2. 重定向输出流
        3. 2.3.3.3. 自动化流重定向
      4. 2.3.4. 版本独立的打印
      5. 2.3.5. 为什么要注意print和stdout
    4. 2.4. 本章习题
  3. 3. 第十二章 if测试和语法规则
    1. 3.1. if语句
      1. 3.1.1. 通用格式
      2. 3.1.2. 基本例子
      3. 3.1.3. 多路分支
    2. 3.2. Python语法规则
      1. 3.2.1. 代码块分隔符
        1. 3.2.1.1. 避免混合使用制表符和空格:Python 3.0中心的错误检查
      2. 3.2.2. 语句的分隔符
        1. 3.2.2.1. 一些特殊情况
    3. 3.3. 真值测试
    4. 3.4. if/else三元表达式
    5. 3.5. 为什么要在意布尔值
    6. 3.6. 本章习题
  4. 4. 第十三章 while和for循环
    1. 4.1. while循环
      1. 4.1.1. 一般格式
      2. 4.1.2. 例子
      3. 4.1.3. break、continue、pass和循环else
        1. 4.1.3.1. 一般循环格式
        2. 4.1.3.2. pass
        3. 4.1.3.3. continue
        4. 4.1.3.4. break
        5. 4.1.3.5. 循环else
          1. 4.1.3.5.1. 关于循环else分句的更多内容
      4. 4.1.4. 为什么要在意”模拟C语言的while循环”
    2. 4.2. for循环
      1. 4.2.1. 一般格式
      2. 4.2.2. 例子
        1. 4.2.2.1. 基本应用
        2. 4.2.2.2. 其他数据类型
        3. 4.2.2.3. 在for循环中的元组赋值
        4. 4.2.2.4. Python 3.0在for循环中拓展的序列赋值
        5. 4.2.2.5. 嵌套for循环
      3. 4.2.3. 为什么要在意”文件扫描”
    3. 4.3. 编写循环的技巧
      1. 4.3.1. 循环计数器:while和range
      2. 4.3.2. 非完备遍历:range和分片
      3. 4.3.3. 修改列表:range
      4. 4.3.4. 并行遍历:zip和map
        1. 4.3.4.1. Python 2.6中的map的等价形式
        2. 4.3.4.2. 使用zip构造字典
      5. 4.3.5. 产生偏移和元素:enumerate
    4. 4.4. 本章习题
  5. 5. 第十四章 迭代器和解析,第一部分
    1. 5.1. 迭代器:初探
      1. 5.1.1. 文件迭代器
      2. 5.1.2. 手动迭代:iter和next
      3. 5.1.3. 其他内置类型迭代器
    2. 5.2. 列表解析:初探
      1. 5.2.1. 列表解析基础知识
      2. 5.2.2. 在文件上使用列表解析
      3. 5.2.3. 拓展的列表解析语法
    3. 5.3. 其他迭代环境
    4. 5.4. Python 3.0中的新的可迭代对象
      1. 5.4.1. range迭代器
      2. 5.4.2. map、zip和filter迭代器
      3. 5.4.3. 多个迭代器 VS 单个迭代器
      4. 5.4.4. 字典视图迭代器
    5. 5.5. 其他迭代器主题
    6. 5.6. 本章习题
  6. 6. 第十五章 文档
    1. 6.1. Python文档资源
      1. 6.1.1. #注释
      2. 6.1.2. dir函数
      3. 6.1.3. 文档字符串:_doc_
        1. 6.1.3.1. 用户定义的文档字符串
        2. 6.1.3.2. 文档字符串标准
        3. 6.1.3.3. 内置文档字符串
      4. 6.1.4. PyDoc:help函数
      5. 6.1.5. PyDoc:HTML函数
        1. 6.1.5.1. GUI模式PyDoc的启动
        2. 6.1.5.2. PyDoc技巧
      6. 6.1.6. 标准手册集
      7. 6.1.7. 网络资源
      8. 6.1.8. 已出版的书籍
    2. 6.2. 常见编写代码的陷阱
    3. 6.3. 本章习题