第5章 条件与循环
赋值魔法
5.2.1 序列解包
>>> x, y, z = 1, 2, 3
>>> x, y, z = z, x, y
# 交换多个变量的值, 称为序列解包:将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中
>>> values = 1, 2, 3
>>> values
(1, 2, 3)
>>> x, y, z = values
>>> x
1
# 在使用返回元组(或其他序列或可迭代对象的函数或方法时很有用
>>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'
# 可使用星号运算符(*)来收集多余的值,这样无需确保值和变量的个数相同
>>> a, b, *rest = [1, 2, 3, 4]
>>> rest
[3, 4]
# 还可将带星号的变量放在其他位置。
>>> name = "Albus Percival Wulfric Brian Dumbledore"
>>> first, *middle, last = name.split()
>>> middle
['Percival', 'Wulfric', 'Brian']
# 赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。在变量和值的个数相同时亦如此。
>>> a, *b, c = "abc"
>>> a, b, c
('a', ['b'], 'c')
# 这种收集方式也可用于函数参数列表中(参见第6章)。
5.2.2 链式赋值
x = y = somefunction()
5.2.3 增强赋值
*= += 等,增强赋值也可用于其他数据类型
5.3 代码块:缩进
缩进的标准做法是只使用空格,且每级缩进4个空格
在很多语言中,都使用一个特殊的单词或字符(如begin或{)来标识代码块的起始位置,并使用另一个特殊的单词或字符(如end或})来标识结束位置。在Python中,使用冒号(:)指出接下来是一个代码块,并将该代码块中的每行代码都缩进相同的程度。发现缩进量与之前相同时,你就知道当前代码块到此结束了。
5.4 条件和条件语句
5.4.1 条件
- 下面的值都将被解释器视为假:
False None 0 “” () [] {}
换而言之,标准值False和None、各种类型(包括浮点数、复数等)的数值0、空序列(如空字符串、空元组和空列表)以及空映射(如空字典)都被视为假,而其他各种值都被视为真 ① ,包括特殊值True ② 。
① 至少对内置类型值来说如此。你在第9章将看到,对于自己创建的对象,解释为真还是假由你决定。
② 正如Python老手Laura Creighton指出的,这种差别类似于“有些东西”和“没有东西”的差别,而不是真和假的差别。 - 布尔值True和False属于类型bool,而bool与list、str和tuple一样,可用来转换其他的值。
注意 虽然[]和"“都为假(即bool([]) == bool(”") == False),但它们并不相等(即[] != “”)。对其他各种为假的对象来说,情况亦如此(一个更显而易见的例子是() != False)。
5.4.2 语句
str.endswith()
name = input('What is your name?')
if name.endswith('Gumby'):
print('Hello, Mr. Gumby')
else:
print('Hello, stranger')
条件表达式——C语言中三目运算符的Python版本。
下面的表达式使用if和else确定其值:
status = "friend" if name.endswith("Gumby") else "stranger"
5.4.6 更复杂的条件
表5-1 Python比较运算符
表 达 式 | 描 述 |
---|---|
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 is y | x和y是同一个对象 |
x is not y | x和y是不同的对象 |
x in y | x是容器(如序列)y的成员 |
x not in y | x不是容器(如序列)y的成员 |
与赋值一样,Python也支持链式比较:可同时使用多个比较运算符,如0 < age < 100。
- 相等(==)而不是(=)
- 相同运算符:is
- in:成员资格运算符
- 字符串和序列的比较
字符串是依据字符的字母排列顺序进行比较的,字母都是Unicode字符,是按码点排列的,要获悉字母的顺序值,可以使用函数ord()。这个函数的作用与函数chr()相反。
>>> ord("★")
9733
>>> chr(9733)
'★'
短路逻辑和条件表达式
布尔运算符有个有趣的特征:只做必要的计算。例如,仅当x和y都为真时,表达式x and y才为真。因此如果x为假,这个表达式将立即返回假,而不关心y。实际上,如果x为假,这个表达式将返回x,否则返回y。(这将提供预期的结果,你明白了其中的原理吗?)这种行为称为短路逻辑(或者延迟求值):布尔运算符常被称为逻辑运算符,如你所见,在有些情况下将“绕过”第二个值。对于运算符or,情况亦如此。在表达式x or y中,如果x为真,就返回x,否则返回y。(你明白这样做合理的原因吗?)请注意,这意味着位于布尔运算符后面的代码(如函数调用)可能根本不会执行。像下面这样的代码就利用了这种行为:
name = input(‘Please enter your name: ‘) or ‘’
如果没有输入名字,上述or表达式的结果将为’’。在很多情况下,你都宁愿使用条件表达式,而不耍这样的短路花样。不过前面这样的语句确实有其用武之地。
5.5.4 一些有用的迭代工具
- 并行迭代
- zip: 将两个序列“缝合”起来,并返回一个由元组组成的序列。可使用list将其转换为列表。当序列的长度不同时,函数zip将在最短的序列用完后停止“缝合”。
list(zip(name, ages))
for name, age in zip(names, ages):
print(name, 'is', age, 'years old')
- 迭代时获取索引
例如:替换一个字符串列表中所有包含子串’xxx’的字符串。
for string in strings:
if 'xxx' in string:
index = strings.index(string) #在字符串中查找字符串
strings[index] = '[censored]'
这可行,但替换前的搜索好像没有必要。另外,如果没有替换,搜索返回的索引可能不对(即返回的是该字符串首次出现处的索引)。下面是一种更佳的解决方案:
index = 0
for string in strings:
if 'xxx' in string:
strings[index] = '[censored]'
index += 1
这个解决方案虽然可以接受,但看起来也有点笨拙。另一种解决方案是使用内置函数enumerate。
for index, string in enumerate(strings):
if 'xxx' in string:
strings[index] = '[censored]'
- 反向迭代和排序后再迭代
reversed()和sorted()。它们类似于列表方法reverse和sort,但可用于任何序列或可迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。
>>> sorted([4, 3, 6, 8, 3])
[3, 3, 4, 6, 8]
>>> sorted('Hello, world!')
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello, world!'))
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello, world!'))
'!dlrow ,olleH'
请注意,sorted返回一个列表,而reversed像zip那样返回一个更神秘的可迭代对象。你不能对它执行索引或切片操作,也不能直接对它调用列表的方法。要执行这些操作,可先使用list对返回的对象进行转换。
提示 要按字母表排序,可先转换为小写。为此,可将sort或sorted的key参数设置为str.lower。例如,
>>> sorted("aBc", key=str.lower)
>>> ['a', 'B', 'c']。
5.5.5 调出循环
- break
- continue
- while True/break
5.6 简单推导
列表推导是一种从其他列表创建列表的方式
>>> [x*x for x in range(10) if x 3 == 0] %
[0, 9, 36, 81]
# 还可添加更多的for部分。
>>> [(x, y) for x in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
5.7 del pass、exec和eval
警告 本节介绍如何执行存储在字符串中的Python代码,这样做可能带来严重的安全隐患。如果将部分内容由用户提供的字符串作为代码执行,将无法控制代码的行为。在网络应用程序,如第15章将介绍的通用网关接口(CGI)脚本中,这样做尤其危险。
5.8 本章介绍的新函数
函 数 | 描 述 |
---|---|
chr(n) | 返回一个字符串,其中只包含一个字符,这个字符对应于传入的顺序值n(0 ≤n < 256) |
eval(source[,globals[,locals]]) | 计算并返回字符串表示的表达式的结果 |
exec(source[, globals[, locals]]) | 将字符串作为语句执行 |
enumerate(seq) | 生成可迭代的索引-值对 |
ord© | 接受一个只包含一个字符的字符串,并返回这个字符的顺序值(一个整数) |
range([start,] stop[, step]) | 创建一个由整数组成的列表 |
reversed(seq) | 按相反的顺序返回seq中的值,以便用于迭代 |
sorted(seq[,cmp][,key][,reverse]) | 返回一个列表,其中包含seq中的所有值且这些值是经过排序的 |
xrange([start,] stop[, step]) | 创建一个用于迭代的xrange对象 |
zip(seq1, seq2,…) | 创建一个适合用于并行迭代的新序列 |