if语句
最让人耳熟能详的应该是 if 语句。例如:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
if 语句包含零个或多个 elif 子句及可选的 else 子句。关键字’elif’ 是’else if’ 的缩写,适用于避免过
多的缩进。if … elif … elif … 序列可以当作其他语言中 switch 或 case 语句的替代品。
如果要把一个值与多个常量进行比较,或者检查特定类型或属性,match 语句更实用。详见
match
语句。
for语句
Python 的 for 语句与 C 或 Pascal 中的不同。Python 的 for 语句不迭代算术递增数值(如 Pascal),或是
给予用户定义迭代步骤和暂停条件的能力(如 C),而是迭代列表或字符串等任意序列,元素的迭代顺序
与在序列中出现的顺序一致。例如:
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
遍历集合时修改集合的内容,会很容易生成错误的结果。因此不能直接进行循环,而是应遍历该集合的
副本或创建新的集合:
# Create a sample collection
users = {'Hans': 'active', 'Éléonore': 'inactive', '景 太 郎': 'active'}
# Strategy: Iterate over a copy
for user, status in users.copy().items():
if status == 'inactive':
del users[user]
# Strategy: Create a new collection
active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status
range语句
内置函数 range() 常用于遍历数字序列,该函数可以生成算术级数:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
生成的序列不包含给定的终止数值;range(10) 生成 10 个值,这是一个长度为 10 的序列,其中的元素
索引都是合法的。range 可以不从 0 开始,还可以按指定幅度递增(递增幅度称为’ 步进’,支持负数):
>>> list(range(5, 10))
[5, 6, 7, 8, 9]
>>> list(range(0, 10, 3))
[0, 3, 6, 9]
>>> list(range(-10, -100, -30))
[-10, -40, -70]
range() 和 len() 组合在一起,可以按索引迭代序列:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
不过,大多数情况下,enumerate() 函数更便捷,详见循环的技巧 。
如果只输出 range,会出现意想不到的结果:
>>> range(10)
range(0, 10)
range() 返回对象的操作和列表很像,但其实这两种对象不是一回事。迭代时,该对象基于所需序列返
回连续项,并没有生成真正的列表,从而节省了空间。
这种对象称为可迭代对象
iterable
,函数或程序结构可通过该对象获取连续项,直到所有元素全部迭代完
毕。for 语句就是这样的架构,sum() 是一种把可迭代对象作为参数的函数:
>>> sum(range(4)) # 0 + 1 + 2 + 3
6
循环中的 break、continue 语句及 else 子句
break 语句和 C 中的类似,用于跳出最近的 for 或 while 循环。
循环语句支持 else 子句;for 循环中,可迭代对象中的元素全部循环完毕,或 while 循环的条件为假
时,执行该子句;break 语句终止循环时,不执行该子句。请看下面这个查找素数的循环示例:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(没错,这段代码就是这么写。仔细看:else 子句属于 for 循环,
不属于
if 语句。)
与 if 语句相比,循环的 else 子句更像 try 的 else 子句:try 的 else 子句在未触发异常时执行,循
环的 else 子句则在未运行 break 时执行。try 语句和异常详见异常的处理。
continue 语句也借鉴自 C 语言,表示继续执行循环的下一次迭代:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found an odd number", num)
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9
pass 语句
pass 语句不执行任何操作。语法上需要一个语句,但程序不实际执行任何动作时,可以使用该语句。例
如:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
下面这段代码创建了一个最小的类:
>>> class MyEmptyClass:
... pass
...
pass 还可以用作函数或条件子句的占位符,让开发者聚焦更抽象的层次。此时,程序直接忽略 pass:
>>> def initlog(*args):
... pass # Remember to implement this!
...
match 语句
匹配语句接受一个表达式,并将其值与作为一个或多个案例块给出的连续模式进行比较。这在表面上类似于C语言、Java或JavaScript(以及许多其他语言)中的切换语句,但它更类似于Rust或Haskell等语言中的模式匹配。只有匹配的第一个模式会被执行,它还可以从值中将组件(序列元素或对象属性)提取到变量中。
def http_error(status):
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong with the internet"
注意最后一个代码块:“变量名”_ 被作为 通配符并必定会匹配成功。如果没有 case 语句匹配成功,则
不会执行任何分支。
使用 | (“or ”)在一个模式中可以组合多个字面值:
case 401 | 403 | 404:
return "Not allowed"
模式的形式类似解包赋值,并可被用于绑定变量:
# point is an (x, y) tuple
match point:
case (0, 0):
print("Origin")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
raise ValueError("Not a point")
请仔细研究此代码!第一个模式有两个字面值,可以看作是上面所示字面值模式的扩展。但接下来的两
个模式结合了一个字面值和一个变量,而变量
绑定
了一个来自目标的值(point)。第四个模式捕获了
两个值,这使得它在概念上类似于解包赋值 (x, y) = point。
如果使用类实现数据结构,可在类名后加一个类似于构造器的参数列表,这样做可以把属性放到变量里:
class Point:
x: int
y: int
def where_is(point):
match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Y={y}")
case Point(x=x, y=0):
print(f"X={x}")
case Point():
print("Somewhere else")
case _:
print("Not a point")
可在 dataclass 等支持属性排序的内置类中使用位置参数。还可在类中设置 __match_args__ 特殊属性
为模式的属性定义指定位置。如果它被设为 (”x”, ”y”),则以下模式均为等价的,并且都把 y 属性绑定到
var 变量:
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
读取模式的推荐方式是将它们看做是你会在赋值操作左侧放置的内容的扩展形式,以便理解各个变量
将会被设置的值。只有单独的名称(例如上面的 var)会被 match 语句所赋值。带点号的名称 (例如
foo.bar)、属性名称(例如上面的 x= 和 y=)或类名称(通过其后的”(…)” 来识别,例如上面的 Point)
都绝不会被赋值。
模式可以任意地嵌套。例如,如果有一个由点组成的短列表,则可使用如下方式进行匹配:
match points:
case []:
print("No points")
case [Point(0, 0)]:
print("The origin")
case [Point(x, y)]:
print(f"Single point {x}, {y}")
case [Point(0, y1), Point(0, y2)]:
print(f"Two on the Y axis at {y1}, {y2}")
case _:
print("Something else")
为模式添加成为守护项的 if 子句。如果守护项的值为假,则 match 继续匹配下一个 case 语句块。注意,
值的捕获发生在守护项被求值之前:
match point:
case Point(x, y) if x == y:
print(f"Y=X at {x}")
case Point(x, y):
print(f"Not on the diagonal")
match 语句的其他特性:
• 与解包赋值类似,元组和列表模式具有完全相同的含义,并且实际上能匹配任意序列。但它们不能
匹配迭代器或字符串。
• 序列模式支持扩展解包操作:[x, y, *rest] 和 (x, y, *rest) 的作用类似于解包赋值。在
* 之后的名称也可以为 _,因此,(x, y, *_) 可以匹配包含至少两个条目的序列,而不必绑定其
余的条目。
• 映射模式:{“bandwidth”: b, “latency”: l} 从字典中捕获 “bandwidth” 和 “latency”
的值。与序列模式不同,额外的键会被忽略。**rest 等解包操作也支持。但 **_ 是冗余的,不允
许使用。
• 使用 as 关键字可以捕获子模式:
case (Point(x1, y1), Point(x2, y2) as p2): ...
将把输入的第二个元素捕获为 p2 (只要输入是包含两个点的序列)
• 大多数字面值是按相等性比较的,但是单例对象 True, False 和 None 则是按标识号比较的。
• 模式可以使用命名常量。这些命名常量必须为带点号的名称以防止它们被解读为捕获变量:
from enum import Enum
class Color(Enum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
match color:
case Color.RED:
print("I see red!")
case Color.GREEN:
print("Grass is green")
case Color.BLUE:
print("I'm feeling the blues :(")
学习更多请关注白龙教程小程序和白龙教程公众号