轻轻松松搞定python--5

流程控制

流程控制就是控制代码的执行顺序。
正常代码都是按顺序一行一行执行的,这叫顺序流程。
如果想控制程序在满足某个条件时才执行特定的代码块,就需要条件控制。
如果想控制程序在满足某个条件时重复执行特定的代码块,就需要循环控制。

顺序

上面讲完了。

条件

使用关键字 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')   

循环

使用 whilefor...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的值好像被保存起来了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,714评论 0 38
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,970评论 0 13
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,713评论 0 10
  • Python 是一种相当高级的语言,通过 Python 解释器把符合语法的程序代码转换成 CPU 能够执行的机器码...
    Python程序媛阅读 1,889评论 0 3
  • Python语言特性 1 Python的函数参数传递 看两个如下例子,分析运行结果: 代码一: a = 1 def...
    伊森H阅读 3,042评论 0 15