流程控制
流程控制就是控制代码的执行顺序。
正常代码都是按顺序一行一行执行的,这叫顺序流程。
如果想控制程序在满足某个条件时才执行特定的代码块,就需要条件控制。
如果想控制程序在满足某个条件时重复执行特定的代码块,就需要循环控制。
顺序
上面讲完了。
条件
使用关键字 if...elif...else
来构造条件流程,其中elif和else是可选的,每一个条件判断语句后面以冒号':'结尾,条件成立时执行代的码块跟在下一行并缩进。注意代码块不能全是空行或注释,如果想什么也不做,可以写个pass
。
python 没有像其他语言的 switch...case
关键字。
if x == 0: # 条件不需要括号包起来,行尾要有个冒号
print('x等于0') # 需要缩进,一般建议4个空格,且行尾不需要分号
elif x > 0: # elif 代码块可以多个
print('x大于0')
else: # 当前面所有条件都不满足时,执行下面的代码块
print('x小于0')
if x is None: # 判断 x 等于 None, is用于判断两个对象的id值是否一样,id一样表示是同一个对象
pass # pass表示什么也不做
if x is not None: # 判断 x 不等于 None, not表示取反
print('x = ', x)
条件语句可以使用括号,从而改变条件的判断顺序。
if x == 0 or (y == 0 and z == 0):
print('x等于0')
如果条件语句太长,不好放在一行,可以用括号包起来,括号里的语句可以换行
if (x == 0 or y == 0
or z == 0): # 括号包起来后可以换行
print('x等于0')
或者
if (x == 0
or y == 0
or z == 0
): # 括号包起来后可以换行
print('x等于0')
循环
使用 while
或 for...in
来构造循环流程。
- while
只要 while 后面的条件语句成立,就执行后面跟着的缩进代码块, 下面例子的输出 0 ~ 9 整数。
x = 0
whlile x < 10: # 当变量 x < 10 时,执行下面缩进的两行代码,并再次回到本语句,直到 x >= 10
print(x)
x += 1
所有循环条件语句同样可以使用括号和行号。
- for...in
for...in
相对特殊一点,先看例子, 下面例子的输出 0 ~ 9 整数。
for x in range(10): # 对每个变量 x 值(0 <= x < 10,步长为1, 即等于range(10)的元素值), 执行下面缩进的代码
print(x)
for x in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: # 对每个变量 x 值 (x 等于列表的元素值), 执行下面缩进的代码
print(x)
for...in
循环在 python 中的使用频率很高,而且其背后有一些复杂的约定和机制,完全讲清楚这些需要后面'面向对象'章节的相关知识,这里先大致说一下。
在 for...in
循环中,不是什么东西都可以放在关键字 in
后面,必须是一个可迭代对象(迭代就是一个一个遍历获取的意思),可迭代对象有两个特点:
1 对象可以作为参数传给内置函数 iter() 并返回一个迭代器对象。
2 返回的迭代器对象有__next__()方法, 调用这个方法一次可以返回一个值,调用一次返回一个,直到没有可返回的了,则抛出一个异常(什么是异常后面会讲)。
实际上for...in
循环就是执行上面1、2个步骤来实现循环的,以 for x in range(10)
为例,背后可以分解为下面几个步骤:
1 iter(range(10)) 返回一个迭代器对象,假设为a.
2 循环调用 a.__next__(), 并将返回值赋给 x,直到出现异常则退出循环。
- continue 和 break
continue
用来让循环中的某一次处理提前结束,并进行下一轮。
for x in range(10):
if x == 5: # x == 5时,直接返回for语句,下面的print(x)将不会执行,也就是只打印 0 ~ 4,6 ~ 9
continue
print(x)
break
用来结束整个循环。
for x in range(10):
if x == 5: # x == 5时,将退出整个循环,也就是只打印 0 ~ 4
break
print(x)
函数
把一部分具有相关性的代码封装起来,用一个名字来代替它,这就是函数,当需要使用这部分代码时,不需要把这些代码再写一遍,只要按一定书写规则调用函数就可以了。
函数能提高代码重复利用率,并能提高代码可读性,可以说稍微复杂一点的代码都离不开函数。
定义函数
使用关键字def来定义函数,函数可以有参数 (写在括号里,多个参数用逗号隔开) 和返回值 (使用关键字 return) ,没有明确写返回值的则默认返回 None。
def sum(a, b): # 定义求和函数 sum, a、b 为输入参数
s = a + b
return s # s 为返回值
def hello(): # 定义函数 hello, 没有输入参数
print('hello')
# 没有显式的返回值,则默认返回None
调用函数
调用函数和数学里的用法类似,写上函数名和参数就可以, 比如调用前面例子里的函数。
v = sum(1, 2) # 调用函数 sum 计算 1 和 2 的和,并把返回值 3 赋值给 v
print(sum(1, 2)) # 打印 sum(1, 2) 的返回值 3
hello() # 调用函数 hello,将打印 'hello'
函数内部可以再调用函数,甚至可以调用自己,调用自己的函数叫作递归函数。
程序在进入函数内部时,会分配一定的内存(叫栈帧),在函数退出时,释放这些内存,所以如果函数调用层次太深(特别是递归调用)的话,可能会消耗太多内存,导致程序挂掉。
参数传递
把一个变量作为参数传递给函数,在函数内部看到的变量实际不是函数外部的那个变量,而是一个重新创建的变量,只是变量所引用的对象和函数外部变量所引用的对象一样。因此在函数内部对参数变量的修改,并不会改变函数外部变量的值,但是,如果函数内部改变了参数所引用对象的值,则函数外部变量所引用对象的值也会跟着改变。
>>> def clear(a):
... a = 0 # 这里的 a 和函数外部的 a 不是同一个变量
...
>>> a = 10
>>> clear(a)
>>> a # a 的值并没有被改变
10
>>>
>>> def set(a):
... a[0] = 0 # 改变参数变量引用对象的值
...
>>> a = [10, 10, 10]
>>> set(a)
>>> a # a 引用对象的值被改变了
[0, 10, 10]
>>>
python 的函数支持非常灵活的参数定义和传递方式。
-
位置参数
位置参数(Required arguments), 指调用函数时,参数的个数和顺序必须和定义时严格一样。def foo(a, b, c): print(a, b, c) foo(1, 2, 3) # 1, 2, 3将分别被传给函数参数的a, b, c foo(1,2) # 少一个参数,运行将报错
-
可变参数
可变参数(Variable-length arguments), 指函数的参数个数是可变的,每次调用传入的参数个数可以不一样。
如果同时有位置参数和可变参数,可变参数要在后面。def foo(a, b, *args): # args前面有个'*'表示是可变参数, 可变参数一般建议取名为args print(a, b) for v in args: # 可变参数实际上会被组装成列表,再传递给函数 print(v) foo(1, 2) # 可变参数个数为0 foo(1, 2, 3, 4, 5) # 可变参数个数为3, 分别是3, 4, 5 a = [3, 4, 5] foo(1, 2, *a) # 可以把列表传给可变参数,前面要加个*, 这里效果和foo(1, 2, 3, 4, 5)一样
-
关键字参数
关键字参数(Keyword arguments), 指传递函数参数时,必须通过赋值形式显示写上参数名, 关键字参数传递顺序可以和定义顺序不同。
关键字参数的定义跟在可变参数后面,如果没有可变参数,则在关键字参数前面定义一个 * 。def foo(a, b, *args,c, d): # 可变参数后面的参数 c 和 d 为关键字参数 print(a, b) # 位置参数 for v in args: # 可变参数 print(v) print(c, d) # 关键字参数 foo(1, 2, 3, 4, 5, d=7, c=6) # a为1,b为2,args为(3, 4, 5), c为6,d为7 def foo(a, b, *,c, d): # '*' 后面的参数 c 和 d 为关键字参数 print(a, b, c, d) foo(1, 2, c=6, d=7) # a为1,b为2, c为6,d为7
另外,位置参数实际上也可以用关键字参数的方式传递。
def foo(a, b, c): # 位置参数 print(a, b, c) foo(b=2, a=1, c=3) # 以关键字方式传递参数,1, 2, 3将分别被传给函数参数的a, b, c foo(a=1, b=2) # 少一个参数,运行将报错
以上方式要求传递参数时,参数个数必须和定义时个数一样,如果想定义个数可变的关键字参数,可以在关键字参数前加两个 * 参数。
def foo(a, b, **kwargs): # kwargs前面有两个'*',表示是可变关键字参数, 可变关键字参数一般建议取名为kwargs print(a, b) for k, v in kwargs.items(): # 可变关键字参数实际上会被组装成字典,再传递给函数 print(v) foo(1, 2) # 可变关键字参数个数为0 foo(1, 2, e=3, f=4, g=5) # 可变关键字参数个数为3, 分别是'e':3, 'f':4, 'g':5 a = {'e':3, 'f':4, 'g':5} foo(1, 2, **a) # 可以把字典给可变关键字参数,前面要加两个*, 这里效果和foo(1, 2, e=3, f=4, g=5)一样
-
默认参数
位置参数和关键字参数都可以有默认值,调用函数时,如果没有显示参入参数值,则该参数值为指定的默认值。
有默认值的位置参数必须在没有默认值的位置参数后面,有默认值的关键字参数则没有此限制。def foo(a, b, c=3): # 参数c为默认参数,默认值为3 print(a, b, c) foo(1, 2) # 相当于 foo(1, 2, 3) def foo(a, b, *, c=3, d): # 参数c为默认参数,默认值为3 print(a, b, c, d) foo(1, 2, d=4) # 相当于 foo(1, 2, c=3, d=4) def foo(a, b=2, *, c=3, d): # 参数b为默认参数,默认值为2, 参数c为默认参数,默认值为3 print(a, b, c, d) foo(1, d=4) # 相当于 foo(1, 2, c=3, d=4)
-
任意参数函数
下面形式的函数,可以接受任意参数, 这种函数参数定义方式在实现一些通用的高级函数时很有用。def foo(*args, **kwargs): print(args, kwargs)
多返回值
函数可以返回任意数量的返回值,多个返回值使用逗号隔开。
def test(a, b, c):
return a+1, b+2, c+3
a, b, c = test(1, 1, 1) # a为2,b为3,c为4
调用多返回值的函数时,如果其中有些返回值是不关心的,可以使用下划线'_'作为变量。
a, _, _ = test(1, 1, 1) # a为2,其他返回值不关心
多返回值实际上是以列表的形式返回的,因此也可以只用一个变量接收函数的多返回值。
a = test(1, 1, 1) # a为(2, 3, 4)
函数返回的多个返回值可以直接赋值给多个变量的原因是列表可以直接赋值给多个变量。
a = [1, 2, 3]
i, j, k = a # i为1, j为2, k为3
匿名函数
有些小函数特别是只被一个地方调用的函数,有时候还要为它想一个名字就显得很不方便。python支持定义匿名函数(anonymous function),用来解决这类问题。匿名函数用关键字 'lambda' 来定义。
lambda [arg1 [,arg2,.....argn]]:expression # 不能有return,函数返回值为表达式值
前面说过python里一切皆对象,函数也是对象, 因此可以直接赋值给变量或作为参数传递给其他函数,匿名函数一般就是用在这种场景。
sum = lambda arg1, arg2: arg1 + arg2; # 定义求和匿名函数并赋值给变量sum,arg1、arg2为参数
sum(1, 2) # 调用变量sum,就是调用求和匿名函数
def do(operation, a, b): # 参数operation是一个函数
return operation(a, b)
do(lambda arg1, arg2: arg1 * arg2, 1, 2) # 计算1、2的乘积,匿名函数作为参数传递
函数内定义函数
python函数内可以再定义函数,内部的函数可以访问外部函数的变量,内部函数只能在外部函数内调用,不能在外部函数外调用。
如果外部函数外想调用内部函数,可以把内部函数当成返回值返回,外面通过返回值来调用内部函数,这种用法其实是一种很特殊的机制,有个专门名词叫‘闭包’,远不是函数调用那么简单,后面会专门描述。
def outer_func(a):
b = 2
def inner_func(): # 定义内部函数
return a + b # 内部函数调用外部函数的变量a和b
print(inner_func())
print(outer_func(1)) # 打印 3
def outer_func(a):
b = 2
def inner_func():
return a + b
return inner_func # 返回内部函数
func = outer_func(1) # 通过返回值调用内部函数
print(func()) # 打印 3,很神奇,outer_func里的变量a和b的值好像被保存起来了