Python教程(二)——控制流工具前半部分
目录
- 1. if语句
- 2. for语句
- 3. range()函数
- 4. break和continue语句
- 5. 循环的else子句(有点抽象)
- 6. pass语句
- 7. match语句
- 参考
1. 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
可有零个或多个elif部分,else部分也是可选的。关键字elif是else if的缩写,用于避免过多的缩进。if…elif…elif…序列可以当作其它语言中switch或case语句的替代品。
如果是把一个值与多个常量进行比较,或者检查特定类型或属性,match语句更有用。
2. for语句
Python的for语句与C或Pascal中的不同。Python的for语句不迭代算术递增数值,或是给予用户定义迭代步骤和结束条件的能力,而是在列表或字符串等任意序列的元素上迭代,按它们在序列中出现的顺序。例如:
>>> # 度量一些字符串
>>> words = [ 'cat', 'window', 'defenestrate' ]
>>> for w in words:
··· print(w, len(w))
···
cat 3
window 6
defenestrate 12
很难正确地在迭代多项集的同时修改多项集的内容。更简单的方法是迭代多项集的副本或者创建新的多项集:
# 创建实例多项集
users = { 'Hans': 'active', 'Eleonore': 'inactive', '景太郎': 'active' }# 策略:迭代一个副本
for user, status in users.copy().items():if status == 'inactive':del users[user]# 策略:创建一个新多项集
active_users = {}
for user, status in users.items():if status == 'active':active_users[user] = status
3. 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
如果直接打印一个range会发生意想不到的事情:
>>> range(10)
range(0, 10)
range()返回的对象在很多方面和列表的行为一样,但其实它和列表不一样。该对象只有在被迭代时才一个一个地返回所期望的列表项,并没有真正生成过一个含有全部项的列表,从而节省了空间。
这种对象称为可迭代对象,适合作为需要获取一系列值的函数或程序构件的参数。for语句就是这样的程序构件;以可迭代对象作为参数的函数例如sum():
>>> sum(range(4)) # 0 + 1 + 2 + 3
6
4. break和continue语句
break语句将跳出最近的一层for或while循环:
>>> for n in range(2, 10):
··· for x in range(2, n):
··· if n % x == 0:
··· print(f"{n} equals {x} * {n // x}")
··· break
···
4 equals 2 * 2
6 equals 2 * 3
8 equals 2 * 4
9 equals 3 * 3
continue语句将继续执行循环的下一次迭代:
>>> for num in range(2, 10)
··· if num % 2 == 0:
··· print(f"Found an even number {num}")
··· continue
··· print(f"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
5. 循环的else子句(有点抽象)
在for或while循环中break语句可能对应一个else子句。如果循环在未执行break的情况下结束,else子句将会执行。
在for循环中,else子句会在循环结束其他最后一次迭代之后,即未执行break的情况下被执行。
在while循环中,它会在循环条件变为假肢后执行。
在这两类循环中,当在循环被break终结时else子句不会被执行。当然,其他提前结束循环的方式,如return或是引发异常,也会跳过else子句的执行。
下面的搜索质数的for循环就是一个例子:
>>> for n in range(2, 10):
··· for x in range(2, n):
··· if n % x == 0:
··· print(n, 'equals', x, '*', n // x)
··· break
··· else:
··· # 循环到底未找到一个因数
··· 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子句的一种方式是想象它对应于循环内的if。当循环执行时,它将运行一系列的if/if/if/else。if位于循环内部,会出现多次。当出现条件为真的。if位于循环内部,会出现多次。当出现条件为真的情况时,将发生break。如果条件一直不为真,则循环外的else子句将被执行。
当配合循环使用时,else子句更像是try语句的else子句而不像if语句的相应子句:一个try语句的else子句在未发生异常时运行,而一个循环的else子句会在未发生break时运行。
6. pass语句
pass语句不执行任何动作。语法上需要一个语句,但程序毋需执行任何动作时,可以使用该语句。例如:
>>> while True:
··· pass # 无限等待键盘中断Crtl+C
···
这常用于创建一个最小的类:
>>> class MyEmptyClass:
··· pass
···
pass还可用作函数或条件语句的占位符,让你保持在更抽象的层次进行思考。pass会被默默地忽略:
>>> def initlog(*args):
··· pass # 记得实现这个!
···
7. match语句
match语句接受一个表达式并把它的值与一个或多个case块给出的一系列模式进行比较。这表面上像C、Java或JavaScript中的switch语句,但其实它更像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匹配成功,则不会执行任何分支。
你可以用|将多个字面值组合到一个模式中:
case 401 | 403 | 404:return 'Not allowed'
形如解包赋值的模式可被用于绑定变量:
# point是一个(x, y)元组
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')
第一个模式有两个字面值,可视为前述字面值模式的扩展;接下来的两个模式结合了一个字面值和一个变量,变量绑定了来自主语的一个值;第四个模式捕获了两个值,使其在概念上与解包赋值(x, y) = point类似.
如果用类组织数据,可以用类名后借一个参数列表这种很像构造器的形式,把属性捕获到变量里:
class Point:def __init__(self, x, y):self.x = xself.y = ydef 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')
一些内置类为属性提供了一个顺序,此时,可以使用位置参数。自定义类可通过在类中设置特殊属性__match_args__,为属性指定其在模式中对应的位置。若设为(“x”, “y”),则以下模式相互等价且都把属性y绑定到变量var:
Point(1, var)
Point(1, y=var)
Point(x=1, y=var)
Point(y=var, x=1)
建议这样来阅读一个模式——通过将其视为赋值语句等号左边的一种扩展形式,来理解各个变量被设为何值。match语句只会为单一的名称(如上面的var)赋值,而不会赋值给带点号的名称(如foo.bar)、属性名(如上面的x=和y=)和类名(如上面的Point)。
模式可以任意嵌套。举例来说,如果我们有一个由Point组成的列表,且Point添加了__match_args__时,我们可以这样来匹配它:
class Point:__match_args__ = ( 'x', 'y' )def __init__(self, x, y):self.x = xself.y = ymatch 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('Not on the diagonal')
该语句的一些其他关键特性:
1. 与解包赋值类似,元组和列表模式具有完全相同的含义并且实际上都能匹配任意序列,区别是它们不能匹配迭代器或字符串。
2. 序列模式支持扩展解包:[x, y, *rest]和(x, y, *rest)和相应的解包赋值做的事是一样的。接在*后的名称也可以为_,所以(x, y, *_)匹配含至少两项的序列,而不必绑定剩余的项。
3. 映射模式:{“bandwidth": b, “latency”: l}从字典中捕获”bandwidth"和“latency”的值。额外的键会被忽略,这一点与序列模式不同。**rest这样的解包也支持,但是**_将会是冗余的,故不允许使用。 **rest放在字典的末尾能忽略其他键值,但是由于映射模式本身就能忽略其他键值,因此**rest是冗余的。
4. 使用as关键字可以捕获子模式:
case (Point(x1, y1), Point(x2, y2) as p2):...
将把输入中的第二个元素捕获为p2,只要输入是包含两个点的序列。
5. 大多数字面值是按相等性比较的,但是单例对象True、False和None是按id比较的。
6. 模式可以使用具名常量。它们必须作为带点号的名称出现,以防止它们被解释为用于捕获的变量:
from enum import Enumclass 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 :(")
参考
https://docs.python.org/zh-cn/3.13/tutorial/controlflow.html