第4章 Python外壳:代码结构
4.1 使用 # 注释
4.2 使用 \ 连接
>>> alphabet = 'abcdefg' + \ 'hijklmnop' + \ 'qrstuv' + \ 'wxyz'
4.3 使用 if、elif 和 else 进行比较
In [3]: if furry: ...: if small: ...: print("it's a cat") ...: else: ...: print("it's a bear") ...: else: ...: if small: ...: print("it's a skink!") ...: else: ...: print("it's a human") ...: it's a cat
如果要检查超过两个条件,可以使用 if 、 elif (即 else if)和 else :
In [4]: color = "puce" In [5]: if color == "red": ...: print("It's a tomato") ...: elif color == "green": ...: print("It's a green pepper") ...: elif color == "bee purple": ...: print("I don't know what it is, but only bees can see it") ...: else: ...: print("I've never heard of the color", color) ...: I've never heard of the color puce
上面的例子中,我们使用了 == 作为判断相等的操作符,Python 中的比较操作符见下表

什么是真值( True )
如果表达式的返回类型不是布尔会发生什么?什么情况下 Python 会认为是 True 和 False ?
一个成假赋值不一定明确表示为 False ,下面的情况也会被认为是 False 。

4.4 使用 while 进行循环
Python 中最简单的循环机制是 while
In [8]: while count <= 5: ...: print(count) ...: count += 1 ...: 1 2 3 4 5
4.4.1 使用 break 跳出循环
如果你想让循环在某一条件下停止,但是不确定在哪次循环跳出,可以在无限循环中声明
break 语句。
In [9]: while True: ...: stuff = input("String to capitalize [type q to quit]: ") ...: if stuff == 'q': ...: break ...: print(stuff.capitalize()) ...: String to capitalize [type q to quit]: test Test String to capitalize [type q to quit]: hey, it works Hey, it works String to capitalize [type q to quit]: q
4.4.2 使用 continue 跳到循环开始
有时我们并不想结束整个循环,仅仅想跳到下一轮循环的开始。下面是一个编造的例子:
读入一个整数,如果它是奇数则输出它的平方数;如果是偶数则跳过。同样使用 q 来结束
循环,代码中加上了适当的注释:
In [10]: while True: ...: value = input("Integer, please [q to quit]: ") ...: if value == 'q': # 停止循环 ...: break ...: number = int(value) ...: if number % 2 == 0: # 判断偶数 ...: continue ...: print(number, "squared is", number*number) ...: Integer, please [q to quit]: 1 1 squared is 1 Integer, please [q to quit]: 2 Integer, please [q to quit]: 3 3 squared is 9 Integer, please [q to quit]: 4 Integer, please [q to quit]: 5 5 squared is 25 Integer, please [q to quit]: q
4.4.3 循环外使用 else
如果 while 循环正常结束(没有使用 break 跳出),程序将进入到可选的 else 段。当你使
用循环来遍历检查某一数据结构时,找到满足条件的解使用 break 跳出;循环结束,即没
有找到可行解时,将执行 else 部分代码段:
In [11]: numbers = [1, 3, 5] In [12]: position = 0 In [13]: while position < len(numbers): ...: number = numbers[position] ...: if number % 2 == 0: ...: print('Found even number', number) ...: break ...: position += 1 ...: else: #没有执行break ...: print('No even number found') ...: No even number found
4.5 使用 for 迭代
Python 频繁地使用迭代器。它允许在数据结构长度未知和具体实现未知的情况下遍历整个
数据结构,并且支持迭代快速读写中的数据,以及允许不能一次读入计算机内存的数据流
的处理。
除了 while 循环意外 ,有一种更优雅的、Python 风格的遍历方式:
In [14]: rabbits = ['Flopsy', 'Mopsy', 'Cottontail', 'Peter'] In [15]: for rabbit in rabbits: ...: print(rabbit) ...: Flopsy Mopsy Cottontail Peter
列表(例如 rabbits )、字符串、元组、字典、集合等都是 Python 中可迭代的对象。元组或
者列表在一次迭代过程产生一项,而字符串迭代会产生一个字符,如下所示:
In [16]: word = 'cat' In [17]: for letter in word: ...: print(letter) ...: c a t
对一个字典(或者字典的 keys() 函数)进行迭代将返回字典中的键。
In [18]: accusation = {'room': 'ballroom', 'weapon': 'lead pipe',
...: 'person': 'Col. Mustard'}
In [19]: for card in accusation: # 或者是for card in accusation.keys():
...: print(card)
...:
room
weapon
person
如果想对字典的值进行迭代,可以使用字典的 values() 函数:
In [20]: for value in accusation.values(): ...: print(value) ...: ballroom lead pipe Col. Mustard
为了以元组的形式返回键值对,可以使用字典的 items() 函数:
In [21]: for item in accusation.items(): ...: print(item) ...: ('room', 'ballroom') ('weapon', 'lead pipe') ('person', 'Col. Mustard')
记住,元组只能被初始化一次,它的值是不能改变的。对于调用函数 items() 返回的每一
个元组,将第一个返回值(键)赋给 card ,第二个返回值(值)赋给 contents :
In [22]: for card, contents in accusation.items(): ...: print('Card', card, 'has the contents', contents) ...: Card room has the contents ballroom Card weapon has the contents lead pipe Card person has the contents Col. Mustard
4.5.1 使用 break 跳出循环
在 for 循环中跳出的用法和在 while 循环中是一样的。
4.5.2 使用 continue 跳到循环开始
在一个循环中使用 continue 会跳到下一次的迭代开始,这一点和 while 循环也是类似的。
4.5.3 循环外使用 else
类似于 while , for 循环也可以使用可选的 else 代码段,用来判断 for 循环是否正常结束
(没有调用 break 跳出),否则会执行 else 段。
当你想确认之前的 for 循环是否正常跑完,增加 else 判断是有用的。
In [23]: cheeses = [] In [24]: for cheese in cheeses: ...: print('This shop has some lovely', cheese) ...: break ...: else: # 当没有执行break ...: print('This is not much of a cheese shop, is it?') ...: This is not much of a cheese shop, is it?
4.5.4 使用 zip() 并行迭代
在使用迭代时,有一个非常方便的技巧:通过 zip() 函数对多个序列进行并行迭代:
In [25]: days = ['Monday', 'Tuesday', 'Wednesday'] In [26]: fruits = ['banana', 'orange', 'peach'] In [27]: drinks = ['coffee', 'tea', 'beer'] In [28]: desserts = ['tiramisu', 'ice cream', 'pie', 'pudding'] In [29]: for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts): ...: print(day, ": drink", drink, "- eat", fruit, "- enjoy", dessert) ...: Monday : drink coffee - eat banana - enjoy tiramisu Tuesday : drink tea - eat orange - enjoy ice cream Wednesday : drink beer - eat peach - enjoy pie
zip() 函数在最短序列“用完”时就会停止。上面例子中的列表( desserts )是最长的,所
以我们无法填充列表,除非人工扩展其他列表。
3.4 节中的 dict() 函数会将两项序列,比如元组、列表、字符串,创建成一个字典,同时
使用 zip() 函数可以遍历多个序列,在具有相同位移的项之间创建元组。下面创建英语单
词和法语单词之间的对应关系的两个元组:
In [30]: english = 'Monday', 'Tuesday', 'Wednesday' In [31]: french = 'Lundi', 'Mardi', 'Mercredi' In [32]: zip(english, french) Out[32]: <zip at 0x1e4d025b488> 现在使用 zip() 函数配对两个元组。函数的返回值既不是元组也不是列表,而是一个整合在一起的可迭代变量: In [33]: list(zip(english, french)) Out[33]: [('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')] 配合 dict() 函数和 zip() 函数的返回值就可以得到一本微型的英法词典: In [34]: dict(zip(english, french)) Out[34]: {'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}
4.5.5 使用 range() 生成自然数序列
range() 函数返回在特定区间的自然数序列,不需要创建和存储复杂的数据结构,例如列
表或者元组。这允许在不使用计算机全部内存的情况下创建较大的区间,也不会使你的程
序崩溃。
range() 函数的用法类似于使用切片: range ( start 、 stop 、 step )。而 start 的默认值为
0 。唯一要求的参数值是 stop ,产生的最后一个数的值是 stop 的前一个,并且 step 的默
认值是 1 。当然,也可以反向创建自然数序列,这时 step 的值为 -1 。
像 zip() 、 range() 这些函数返回的是一个可迭代的对象,所以可以使用 for ... in 的结构
遍历,或者把这个对象转化为一个序列(例如列表)。
In [35]: for x in range(0, 3): ...: print(x) ...: 0 1 2 In [36]: range(0, 3) Out[36]: range(0, 3) In [37]: list(range(0, 3)) Out[37]: [0, 1, 2]
从 2 到 0 反向创建序列: In [38]: for x in range(2, -1, -1): ...: print(x) ...: 2 1 0 In [39]: list(range(2, -1, -1)) Out[39]: [2, 1, 0]
4.6 推导式
推导式是从一个或者多个迭代器快速简洁地创建数据结构的一种方法。它可以将循环和条
件判断结合,从而避免语法冗长的代码。会使用推导式有时可以说明你已经超过 Python 初
学者的水平。也就是说,使用推导式更像 Python 风格。
4.6.1 列表推导式
从 1 到 5 创建一个整数列表,每次增加一项,如下所示
In [40]: number_list = [] In [41]: for number in range(1, 6): ...: number_list.append(number) ...: In [42]: number_list Out[42]: [1, 2, 3, 4, 5]
上面这些方法都是可行的 Python 代码,会得到相同的结果。然而,更像 Python 风格的创
建列表方式是使用列表推导。最简单的形式如下所示:
[ expression for item in iterable ]
下面的例子将通过列表推导创建一个整数列表:
In [43]: number_list = [number for number in range(1, 6)]
In [44]: number_list
Out[44]: [1, 2, 3, 4, 5]
在第一行中,第一个 number 变量为列表生成值,也就是说,把循环的结果放在列表
number_list 中。第二个 number 为循环变量。其中第一个 number 可以为表达式,试试下面
改编的例子:
In [45]: number_list = [number-1 for number in range(1,6)]
In [46]:
In [46]: number_list
Out[46]: [0, 1, 2, 3, 4]
列表推导把循环放在方括号内部。这种例子和之前碰到的不大一样,但却是更为常见的方
式。同样,列表推导也可以像下面的例子加上条件表达式:
[ expression for item in iterable if condition ]
In [47]: a_list = [number for number in range(1,6) if number % 2 == 1] In [48]: a_list Out[48]: [1, 3, 5]
正如存在很多嵌套循环一样,在对应的推导中会有多个 for ... 语句。我们先来看
一个简单的嵌套循环的例子,并在屏幕上打印出结果:
In [49]: rows = range(1,4) In [50]: cols = range(1,3) In [51]: for row in rows: ...: for col in cols: ...: print(row, col) ...: 1 1 1 2 2 1 2 2 3 1 3 2
下面使用一次推导,将结果赋值给变量 cells ,使它成为元组 (row,col)
In [52]: cells = [(row, col) for row in rows for col in cols]
In [53]: for cell in cells:
...: print(cell)
...:
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
另外,在对 cells 列表进行迭代时可以通过元组拆封将变量 row 和 col 的值分别取出:
In [54]: for row, col in cells:
...: print(row, col)
...:
1 1
1 2
2 1
2 2
3 1
3 2
4.6.2 字典推导式
除了列表,字典也有自己的推导式。最简单的例子就像:
{ key_expression : value_expression for expression in iterable }
In [55]: word = 'letters' In [56]: letter_counts = {letter: word.count(letter) for letter in word} In [57]: letter_counts Out[57]: {'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}
程序中,对字符串 'letters' 中出现的字母进行循环,计算出每个字母出现的次数。对于
程序执行来说,两次调用 word.count(letter) 浪费时间,因为字符串中 t 和 e 都出现了两
次,第一次调用 word.count() 时已经计算得到相应的值。下面的例子会解决这个小问题,
更符合 Python 风格:
In [55]: word = 'letters' In [56]: letter_counts = {letter: word.count(letter) for letter in word} In [57]: letter_counts Out[57]: {'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}
将字符串转换成 集合 去重 In [58]: letter_counts = {letter: word.count(letter) for letter in set(word)} In [59]: letter_counts Out[59]: {'r': 1, 'l': 1, 'e': 2, 't': 2, 's': 1}
4.6.3 集合推导式
集合也不例外,同样有推导式。最简单的版本和之前的列表、字典推导类似:
{ expression for expression in iterable }
最长的版本( if tests, multiple for clauses)对于集合而言也是可行的:
In [60]: a_set = {number for number in range(1,6) if number % 3 == 1}
In [61]: a_set
Out[61]: {1, 4}
4.6.4 生成器推导式
元组是没有推导式的。你可能认为将列表推导式中的方括号变成圆括号就可以定义元组推导式,就像下面的表达式一样:
>>> number_thing = (number for number in range(1, 6))
其实,圆括号之间的是生成器推导式,它返回的是一个生成器对象:
>>> type(number_thing
你可以直接对生成器对象进行迭代,如下所示: >>> for number in number_thing: ... print(number) ... 1 2 3 4 5 或者,通过对一个生成器的推导式调用 list() 函数,使它类似于列表推导式: >>> number_list = list(number_thing) >>> number_list [1, 2, 3, 4, 5]
一个生成器只能运行一次。列表、集合、字符串和字典都存储在内存中,但
是生成器仅在运行中产生值,不会被存下来,所以不能重新使用或者备份一
个生成器。
如果想再一次迭代此生成器,会发现它被擦除了:
>>> try_again = list(number_thing) >>> try_again []
你既可以通过生成器推导式创建生成器,也可以使用生成器的函数。后面会介绍这些函
数,并且探讨这些生成器函数的特殊用法。
4.7 函数
到目前为止,我们的 Python 代码已经实现了小的分块。它们都适合处理微小任务,但是我
们想复用这些代码,所以需要把大型代码组织成可管理的代码段。
代码复用的第一步是使用函数,它是命名的用于区分的代码段。函数可以接受任何数字或
者其他类型的输入作为参数,并且返回数字或者其他类型的结果。
你可以使用函数做以下两件事情:
• 定义函数
• 调用函数
我们先定义和调用一个没有参数的函数。下面的例子是最简单的 Python 函数:
In [1]: def do_nothing(): ...: pass
In [2]: def make_a_sound():
...: print('quack')
...:
In [3]: make_a_sound()
quack
In [5]: if agree(): ...: print('Splendid!') ...: else: ...: print('That was unexpected.') ...: Splendid!
在函数中,使用 if 判断和 for/while 循环组合能实现之前无法实现的功能。
这个时候该在函数中引入参数。
In [6]: def echo(anthing): ...: return anthing + ' ' + anthing ...: In [7]: echo('Rumplestiltskin') Out[7]: 'Rumplestiltskin Rumplestiltskin'
在这里依旧沿用评论颜色的代码段。调用 commentary 函数,把 color 作为输入的参
数,使它返回对颜色的评论字符串
In [8]: def commentary(color): ...: if color == 'red': ...: return "It's a tomato" ...: elif color == 'green': ...: return "It's a green pepper" ...: elif color == 'bee purple': ...: return "I don't know what it is, but only bees can see it." ...: else: ...: return "I've never heardd of the color " + color + "." ...: In [9]: comment = commentary('blue') In [10]: print(comment) I've never heardd of the color blue.
一个函数可以接受任何数量(包括 0)的任何类型的值作为输入变量,并且返回任何数量
(包括 0)的任何类型的结果。如果函数不显式调用 return 函数,那么会默认返回 None 。
In [12]: print(do_nothing()) None
有用的 None
None 是 Python 中一个特殊的值,虽然它不表示任何数据,但仍然具有重要的作用。
虽然 None 作为布尔值和 False 是一样的,但是它和 False 有很多差别。下面是一个
例子:
In [13]: thing = None In [14]: if thing: ...: print("It's some thing") ...: else: ...: print("It's nothing") ...: It's nothing 为了区分 None 和布尔值 False , 使用 Python 的 is 操作符: In [15]: if thing is None: ...: print("It's nothing") ...: else: ...: print("It's something") ...: It's nothing
这虽然是一个微妙的区别,但是对于 Python 来说是很重要的。你需要把 None 和不含
任何值的空数据结构区分开来。 0 值的整型 / 浮点型、空字符串( '' )、空列表( [] )、
空元组( (,) )、空字典( {} )、空集合( set() )都等价于 False ,但是不等于 None 。
In [17]: def is_none(thing): ...: if thing is None: ...: print("It's None") ...: elif thing: ...: print("It's True") ...: else: ...: print("It's False") ...: In [18]: is_none(None) It's None In [19]: is_none(True) It's True In [20]: is_none(False) It's False In [21]: is_none(0) It's False In [22]: is_none(0.0) It's False In [23]: is_none(1) It's True In [24]: is_none(()) It's False In [25]: is_none('') It's False In [26]: is_none([]) It's False In [27]: is_none({}) It's False
4.7.1 位置参数
Python 处理参数的方式要比其他语言更加灵活。其中,最熟悉的参数类型是位置参数,传
入参数的值是按照顺序依次复制过去的。
In [28]: def menu(wine, entree, dessert): ...: return{'wine': wine, 'entree': entree, 'dessert': dessert} ...: In [29]: menu('chardonnay', 'chicken', 'cake') Out[29]: {'wine': 'chardonnay', 'entree': 'chicken', 'dessert': 'cake'}
4.7.2 关键字参数
为了避免位置参数带来的混乱,调用参数时可以指定对应参数的名字,甚至可以采用与函
数定义不同的顺序调用:
In [30]: menu(entree='beef', dessert='bagel', wine='bordeaux') Out[30]: {'wine': 'bordeaux', 'entree': 'beef', 'dessert': 'bagel'} 你也可以把位置参数和关键字参数混合起来。首先,实例化参数 wine ,然后对参数 entree 和 dessert 使用关键字参数的方式: In [31]: menu('frontenac', dessert='flan', entree='fish') Out[31]: {'wine': 'frontenac', 'entree': 'fish', 'dessert': 'flan'}
4.7.3 指定默认参数值
当调用方没有提供对应的参数值时,你可以指定默认参数值。这个听起来很普通的特性实
际上特别有用,以之前的例子为例:
In [32]: def menu(wine, entree, dessert='pudding'): ...: return {'wine': wine, 'entree': entree, 'dessert': dessert} ...: In [33]: menu('chardonnay', 'chicken') Out[33]: {'wine': 'chardonnay', 'entree': 'chicken', 'dessert': 'pudding'} 如果你提供参数值,在调用时会代替默认值: In [34]: menu('dunkelfelder', 'duck', 'doughnut') Out[34]: {'wine': 'dunkelfelder', 'entree': 'duck', 'dessert': 'doughnut'}
默认参数值在函数被定义时已经计算出来,而不是在程序运行时。Python 程
序员经常犯的一个错误是把可变的数据类型(例如列表或者字典)当作默认
参数值。
在下面的例子中,函数 buggy() 在每次调用时,添加参数 arg 到一个空的列表 result ,然
后打印输出一个单值列表。但是存在一个问题:只有在第一次调用时列表是空的,第二次
调用时就会存在之前调用的返回值:
In [35]: def buggy(arg, result=[]): ...: result.append(arg) ...: print(result) ...: In [36]: buggy('a') ['a'] In [37]: buggy('b') ['a', 'b'] In [38]: def works(arg): ...: result = [] ...: result.append(arg) ...: return result ...: In [39]: works('a') Out[39]: ['a'] In [40]: works('b') Out[40]: ['b'] 这样的修改也为了表明是第一次调用跳过一些操作: In [43]: def nonbuggy(arg, result=None): ...: if result is None: ...: result = [] ...: result.append(arg) ...: print(result) ...: In [44]: nonbuggy('a') ['a'] In [45]: nonbuggy('b') ['b']
4.7.4 使用 * 收集位置参数
如果你之前使用 C/C++ 编程,可能会认为 Python 中的星号( * )和指针相关。然而,Python 是没有指针概念的。
当参数被用在函数内部时, 星号将一组可变数量的位置参数集合成参数值的元组。在下面
的例子中 args 是传入到函数 print_args() 的参数值的元组:
In [46]: def print_args(*args): ...: print('Positional argument tuple:', args) ...: 无参数调用函数,则什么也不会返回: In [47]: print_args() Positional argument tuple: () 给函数传入的所有参数都会以元组的形式返回输出: In [48]: print_args(3, 2, 1, 'wait!', 'uh...') Positional argument tuple: (3, 2, 1, 'wait!', 'uh...') 这样的技巧对于编写像 print() 一样接受可变数量的参数的函数是非常有用的。如果你的 函数同时有限定的位置参数,那么 *args 会收集剩下的参数:
这样的技巧对于编写像 print() 一样接受可变数量的参数的函数是非常有用的。如果你的
函数同时有限定的位置参数,那么 *args 会收集剩下的参数:
In [49]: def print_more(required1, required2, *args): ...: print('Need this one:', required1) ...: print('Need this one too:', required2) ...: print('All the rest:', args) ...: In [50]: print_more('cap', 'gloves', 'scarf', 'monocle', 'mustache wax') Need this one: cap Need this one too: gloves All the rest: ('scarf', 'monocle', 'mustache wax')
4.7.5 使用 ** 收集关键字参数
使用两个星号可以将参数收集到一个字典中,参数的名字是字典的键,对应参数的值是字
典的值。下面的例子定义了函数 print_kwargs() ,然后打印输出它的关键字参数:
In [51]: def print_kwargs(**kwargs): ...: print('Keyword arguments:', kwargs) ...: In [52]: print_kwargs(wine='merlot', entree='mutton', dessert='macaroon') Keyword arguments: {'wine': 'merlot', 'entree': 'mutton', 'dessert': 'macaroon'}
在函数内部, kwargs 是一个字典。
如果你把带有 *args 和 **kwargs 的位置参数混合起来,它们会按照顺序解析。和 args 一
样,调用时不需要参数 kwargs ,这也是常见用法。
4.7.6 文档字符串
正如《Python 之禅》(the Zen of Python)中提到的,程序的可读性很重要。建议在函数体
开始的部分附上函数定义说明的文档,这就是函数的文档字符串:
In [63]: def echo(anything): ...: 'echo returns its input argument' ...: return anything ...: In [64]: def print_if_true(thing, check): ...: ''' ...: Prints the first argument if a second argument is true. ...: The operation is: ...: 1. Check whether the *second* argument is true. ...: 2. If it is, print the *first* argument. ...: ''' ...: if check: ...: print(thing) ...:
调用 Python 函数 help() 可以打印输出一个函数的文档字符串。把函数名传入函数 help()
就会得到参数列表和规范的文档
In [65]: help(echo) Help on function echo in module __main__: echo(anything) echo returns its input argument 如果仅仅想得到文档字符串: In [66]: print(echo.__doc__) echo returns its input argument
看上去很奇怪的 __doc__ 是作为函数中变量的文档字符串的名字。4.10 节的“名称中 _ 和
__ 的用法”会解释所有下划线背后的原理。
4.7.7 一等公民:函数
之前提过 Python 中一切都是对象,包括数字、字符串、元组、列表、字典和函数。函数是
Python 中的一等公民,可以把它们(返回值)赋给变量,可以作为参数被其他函数调用,
也可以从其他函数中返回值。它可以帮助你在 Python 中实现其他语言难以实现的功能。
为了测试,现在定义一个简单的函数 answer() ,它没有任何参数,仅仅打印输出数字 42 :
In [68]: answer() 42 In [69]: def run_something(func): ...: func() ...: 再定义一个函数 run_something 。它有一个参数 func ,这个参数是一个可以运行的函数的 名字: In [70]: run_something(answer) 42 In [71]: type(run_something) Out[71]: function
定义函数 add_args() ,它会打印输出两个数值参数( arg1 和 arg2 )的和: In [72]: def add_args(arg1, arg2): ...: print(arg1 + arg2) ...: add_args() 的类型是什么? In [73]: type(add_args) Out[73]: function 此刻定义一个函数 run_something_with_args() ,它带有三个参数: • func ——可以运行的函数 • arg1 —— func 函数的第一个参数 • arg2 —— func 函数的第二个参数 In [74]: def run_something_with_args(func, arg1, arg2): ...: func(arg1, arg2) ...: In [75]: run_something_with_args(add_args, 3, 5) 8
同样可以在此用上 *args (位置参数收集)和 **kwargs (关键字参数收集)的技巧。
我们定义一个测试函数,它可以接受任意数量的位置参数,使用 sum() 函数计算它们的和,并返回这个和:
In [76]: def sum_args(*args): ...: return sum(args) ...: ''' sum() 函数,它是 Python 的一个内建函数,用来计算可迭代的数值(整型或 者浮点型)参数的和。''' In [77]: def run_with_positional_args(func, *args): ...: return func(*args) ...: In [78]: run_with_positional_args(sum_args, 1, 2, 3, 4) Out[78]: 10
4.7.8 内部函数
在 Python 中,可以在函数中定义另外一个函数
In [79]: def outer(a, b): ...: def inner(c, d): ...: return c + d ...: return inner(a, b) ...: In [80]: outer(3, 4) Out[80]: 7
4.7.9 闭包
内部函数可以看作一个闭包。闭包是一个可以由另一个函数动态生成的函数,并且可以改
变和存储函数外创建的变量的值。
In [82]: def knight2(saying): ...: def inner2(): ...: return "We are the knights who say: '%s' " % saying ...: return inner2 ...: '''inner2() 函数可以得到 saying 参数的值并且记录下来。 return inner2 这一行返回的是 inner2 函数的复制(没有直接调用)。所以它就是一个闭包:一个被动态创建的可以记录 外部变量的函数。''' '''用不同的参数调用 knights2() 两次''' In [83]: a = knight2('Duck') In [84]: b = knight2('Hasenpfeffer') '''a 和 b 会是什么类型?''' In [85]: type(a) Out[85]: function In [86]: type(b) Out[86]: function '''它们是函数,同时也是闭包:''' In [89]: a Out[89]: <function __main__.knight2.<locals>.inner2()> In [90]: b Out[90]: <function __main__.knight2.<locals>.inner2()> '''如果调用它们,它们会记录被 knights2 函数创建时的外部变量 saying :''' In [87]: a() Out[87]: "We are the knights who say: 'Duck' " In [88]: b() Out[88]: "We are the knights who say: 'Hasenpfeffer' "
4.7.10 匿名函数: lambda() 函数
Python 中,lambda 函数是用一个语句表达的匿名函数。可以用它来代替小的函数。
首先,举一个使用普通函数的例子。定义函数 edit_story() ,参数列表如下所示:
• words ——单词列表
• func ——遍历列表中单词的函数
In [92]: def edit_story(words, func): ...: for word in words: ...: print(func(word)) ...: In [93]: stairs = ['thud', 'meow', 'thud', 'hiss'] In [94]: def enliven(word): ...: return word.capitalize() + '!' ...: In [95]: edit_story(stairs, enliven) Thud! Meow! Thud! Hiss! '''最后,到了 lambda。 enliven() 函数可以简洁地用下面的一个 lambda 代替:''' In [96]: edit_story(stairs, lambda word: word.capitalize() + '!') Thud! Meow! Thud! Hiss!
'''lambda 函数接收一个参数 word 。在冒号和末尾圆括号之间的部分为函数的定义。
通常,使用实际的函数(例如 enliven() )会比使用 lambda 更清晰明了。但是,当需要定
义很多小的函数以及记住它们的名字时,lambda 会非常有用。尤其是在图形用户界面中,
可以使用 lambda 来定义回调函数。'''
python 使用 lambda 来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法
lambda 函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
那它到底有哪些用处呢?
(1)直接赋给一个变量,然后再像一般函数那样调用 In [117]: c = lambda x, y, z: x*y*z In [118]: c(2, 3, 4) Out[118]: 24 当然,也可以在函数后面直接传递实参 In [119]: (lambda x: x**2)(3) Out[119]: 9
(2)将lambda函数作为参数传递给其他函数比如说结合map、filter、sorted、reduce等一些Python内置函数使用,下面举例说明 In [124]: squares = map(lambda x:x**2, range(5)) In [125]: print(list(squares)) [0, 1, 4, 9, 16] '''与sorted函数结合使用,比如:创建由元组构成的列表:''' In [126]: a=[('b',3),('a',2),('d',4),('c',1)] '''按照第一个元素排序''' In [127]: sorted(a, key=lambda x:x[0]) Out[127]: [('a', 2), ('b', 3), ('c', 1), ('d', 4)] '''按照第二个元素排序''' In [128]: sorted(a, key=lambda x:x[1]) Out[128]: [('c', 1), ('a', 2), ('b', 3), ('d', 4)]
(3)嵌套使用将lambda函数嵌套到普通函数中,lambda函数本身做为return的值 In [129]: def increment(n): ...: return lambda x: x+n ...: In [130]: f = increment(4) In [131]: f(2) Out[131]: 6
(4)字符串联合,有默认值,也可以用x=(lambda...)这种格式 In [134]: x = (lambda x='Boo', y='Too', z='Zoo': x+y+z) In [135]: x('Foo') Out[135]: 'FooTooZoo'
(5)在tkinter中定义内联的callback函数 In [136]: import sys In [137]: from tkinter import Button, mainloop In [138]: x = Button(text='Press me',command=(lambda :sys.stdout.write('Hello,World\n'))) In [139]: x.pack() In [140]: x.mainloop()
(6)判断字符串是否以某个字母开头有 In [1]: Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach'] In [2]: B_Name= filter(lambda x: x.startswith('B'),Names) In [3]: print(B_Name)
(7)求两个列表元素的和 In [8]: a = [1,2,3,4] In [9]: b = [5,6,7,8] In [10]: list(map(lambda x,y:x+y, a,b)) Out[10]: [6, 8, 10, 12]
(8)求字符串每个单词的长度 In [11]: sentence = "Welcome To Beijing!" In [12]: words = sentence.split() In [13]: lengths = map(lambda x:len(x),words) In [14]: list(lengths) Out[14]: [7, 2, 8]
4.8 生成器
生成器是用来创建 Python 序列的一个对象。使用它可以迭代庞大的序列,且不需要在内
存中创建和存储整个序列。通常,生成器是为迭代器产生数据的。回想起来,我们已经在
之前的例子中使用过其中一个,即 range() ,来产生一系列整数。 range() 在 Python 2 中
返回一个列表,这也限制了它要进入内存空间。Python 2 中同样存在的生成器 xrange() 在
Python 3 中成为标准的 range() 生成器。这个例子累加从 1 到 100 的整数:
>>> sum(range(1, 101))
5050
每次迭代生成器时,它会记录上一次调用的位置,并且返回下一个值。这一点和普通的函
数是不一样的,一般函数都不记录前一次调用,而且都会在函数的第一行开始执行。
如果你想创建一个比较大的序列,使用生成器推导的代码会很长,这时可以尝试写一个
生成器函数。生成器函数和普通函数类似,但是它的返回值使用 yield 语句声明而不是return 。
下面编写我们自己的 range() 函数版本:
In [15]: def my_range(first=0, last=10, step=1): ...: number == first ...: while number < last: ...: yield number ...: number += step ...: In [16]: my_range Out[16]: <function __main__.my_range(first=0, last=10, step=1)> In [17]: ranger = my_range(1, 5) In [18]: ranger Out[18]: <generator object my_range at 0x0000023A7DE9EDE0> In [19]: for x in ranger: ...: print(x)
4.9 装饰器
有时你需要在不改变源代码的情况下修改已经存在的函数。常见的例子是增加一句调试声
明,以查看传入的参数。
装饰器实质上是一个函数。它把一个函数作为输入并且返回另外一个函数。在装饰器中,
通常使用下面这些 Python 技巧:
• *args 和 **kwargs
• 闭包
• 作为参数的函数
函数 document_it() 定义了一个装饰器,会实现如下功能:
• 打印输出函数的名字和参数的值
• 执行含有参数的函数
• 打印输出结果
• 返回修改后的函数
看下面的代码:
In [24]: def document_it(func): ...: def new_function(*args, **kwargs): ...: print('当前运行的函数', func.__name__) ...: print('位置参数', args) ...: print('关键字参数', kwargs) ...: result = func(*args, **kwargs) ...: print('Result:', result) ...: return result ...: return new_function ...: In [25]: def add_ints(a, b): ...: return a + b ...: In [26]: add_ints(3, 5) Out[26]: 8 In [27]: cooler_add_ints = document_it(add_ints) In [28]: cooler_add_ints(3, 5) 当前运行的函数 add_ints 位置参数 (3, 5) 关键字参数 {} Result: 8 Out[28]: 8
作为对前面人工装饰器赋值的替代,可以直接在要装饰的函数前添加装饰器名字
@decorator_name :
In [29]: @document_it ...: def add_ints(a,b): ...: return a + b ...: In [30]: add_ints(3, 5) 当前运行的函数 add_ints 位置参数 (3, 5) 关键字参数 {} Result: 8 Out[30]: 8
同样一个函数可以有多个装饰器。下面,我们写一个对结果求平方的装饰器 square_it()
In [31]: def square_it(func): ...: def new_function(*args, **kwargs): ...: result = func(*args, **kwargs) ...: return result * result ...: return new_function ...: In [32]: @document_it ...: @square_it ...: def add_ints(a, b): ...: return a + b ...: In [33]: add_ints(3, 5) 当前运行的函数 new_function 位置参数 (3, 5) 关键字参数 {} Result: 64 Out[33]: 64
交换两个装饰器的顺序:
In [34]: @square_it ...: @document_it ...: def add_ints(a, b): ...: return a + b ...: In [35]: add_ints(3, 5) 当前运行的函数 add_ints 位置参数 (3, 5) 关键字参数 {} Result: 8 Out[35]: 64
4.10 命名空间和作用域
一个名称在不同的使用情况下可能指代不同的事物。Python 程序有各种各样的命名空
间,它指的是在该程序段内一个特定的名称是独一无二的,它和其他同名的命名空间
是无关的。
每一个函数定义自己的命名空间。如果在主程序(main)中定义一个变量 x ,在另外一个
函数中也定义 x 变量,两者指代的是不同的变量。但是,天下也没有完全绝对的事情,需
要的话,可以通过多种方式获取其他命名空间的名称。
每个程序的主要部分定义了全局命名空间。因此,在这个命名空间的变量是全局变量。
你可以在一个函数内得到某个全局变量的值:
In [42]: animal = 'fruitbat' In [43]: def print_global(): ...: print('inside print_global:', animal) ...: In [44]: print(animal) fruitbat In [45]: print_global() inside print_global: fruitbat '''但是,如果想在函数中得到一个全局变量的值并且改变它,会报错:''' In [46]: def change_and_print_global(): ...: print('inside change_and_print_global:', animal) ...: animal = 'wombat' ...: print('after the change:', animal) ...: In [47]: change_and_print_global() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-47-f8c8058a7439> in <module> ----> 1 change_and_print_global() <ipython-input-46-1836ba2821c4> in change_and_print_global() 1 def change_and_print_global(): ----> 2 print('inside change_and_print_global:', animal) 3 animal = 'wombat' 4 print('after the change:', animal) 5 UnboundLocalError: local variable 'animal' referenced before assignment
实际上,你改变的另外一个同样被命名为 animal 的变量,只不过这个变量在函数内部:
>>> def change_local(): ... animal = 'wombat' ... print('inside change_local:', animal, id(animal)) ... >>> change_local() inside change_local: wombat 4330406160 >>> animal 'fruitbat' >>> id(animal) 4330390832
'''这里发生了什么?在函数第一行将字符串 fruitbat 赋值给全局变量 animal 。函数 change_
local() 也有一个叫作 animal 的变量。不同的是,它在自己的局部命名空间。
我们使用 Python 内嵌函数 id() 打印输出每个对象的唯一的 ID 值,证明在函数 change_
local() 中的变量 animal 和主程序中的 animal 不是同一个。'''
'''如果在函数中不声明关键字 global ,Python 会使用局部命名空间,同时变量也是局部的。 函数执行后回到原来的命名空间。''' In [48]: animal = 'cat' In [49]: def change_and_print_global(): ...: global animal ...: animal = 'dog' ...: print('inside change_and_print_global: ' , animal) ...: In [50]: animal Out[50]: 'cat' In [51]: change_and_print_global() inside change_and_print_global: dog In [52]: animal Out[52]: 'dog'
Python 提供了两个获取命名空间内容的函数:
• locals() 返回一个局部命名空间内容的字典;
• globals() 返回一个全局命名空间内容的字典。
下面是它们的实例:
In [53]: animal = 'cat' In [54]: def change_local(): ...: animal = 'dog' ...: print('local ', locals()) ...: In [55]: animal Out[55]: 'cat' In [56]: change_local() local {'animal': 'dog'} In [57]: print('globals: ', globals()) globals: {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', "Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']", "B_Name= filter(lambda x: x.startswith('B'),Names) ", 'print(B_Name) ', 'print(B_Name())', 'B_Name()', 'B_Name', "get_ipython().run_line_magic('cls', '')", 'a = [1,2,3,4]', 'b = [5,6,7,8]', 'list(map(lambda x,y:x+y, a,b))', 'sentence = "Welcome To Beijing!"', 'words = sentence.split()', 'lengths = map(lambda x:len(x),words)', 'list(lengths)', 'def my_range(first=0, last=10, step=1):\n number == first\n while number < last:\n yield number\n number += step\n ', 'my_range', 'ranger = my_range(1, 5)', 'ranger', 'for x in ranger:\n print(x)\n ', 'list(ranger)', 'my_range(2, 4)', 'list(my_range(1, 3))', "get_ipython().run_line_magic('cls', '')", "def document_it(func):\n def new_function(*args, **kwargs):\n print('当前运行的函数', func.__name__)\n print('位置参数', args)\n print('关键字参数', kwargs)\n result = func(*args, **kwargs)\n print('Result:', result)\n return result\n return new_function\n ", 'def add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', 'cooler_add_ints = document_it(add_ints)', 'cooler_add_ints(3, 5)', '@document_it\ndef add_ints(a,b):\n return a + b\n ', 'add_ints(3, 5)', 'def square_it(func):\n def new_function(*args, **kwargs):\n result = func(*args, **kwargs)\n return result * result\n return new_function\n ', '@document_it\n@square_it\ndef add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', '@square_it\n@document_it\ndef add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', "animal = 'cat", "animal = 'cat'", "def print_global():\n global animal\n animal = 'dog'\n print(animal)\n ", 'animal', 'print_global()', 'animal', "animal = 'fruitbat'", "def print_global():\n print('inside print_global:', animal)\n ", 'print(animal)', 'print_global()', "def change_and_print_global():\n print('inside change_and_print_global:', animal)\n animal = 'wombat'\n print('after the change:', animal)\n ", 'change_and_print_global()', "animal = 'cat'", "def change_and_print_global():\n global animal\n animal = 'dog'\n print('inside change_and_print_global: ' , animal)\n ", 'animal', 'change_and_print_global()', 'animal', "animal = 'cat'", "def change_local():\n animal = 'dog'\n print('local ', locals())\n ", 'animal', 'change_local()', "print('globals: ', globals())"], '_oh': {6: <filter object at 0x0000023A7BFA14E0>, 10: [6, 8, 10, 12], 14: [7, 2, 8], 16: <function my_range at 0x0000023A7C13B9D8>, 18: <generator object my_range at 0x0000023A7DE9EDE0>, 20: [], 21: <generator object my_range at 0x0000023A7C171048>, 26: 8, 28: 8, 30: 8, 33: 64, 35: 64, 39: 'cat', 41: 'dog', 50: 'cat', 52: 'dog', 55: 'cat'}, '_dh': ['C:\\Users\\gupan'], 'In': ['', "Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']", "B_Name= filter(lambda x: x.startswith('B'),Names) ", 'print(B_Name) ', 'print(B_Name())', 'B_Name()', 'B_Name', "get_ipython().run_line_magic('cls', '')", 'a = [1,2,3,4]', 'b = [5,6,7,8]', 'list(map(lambda x,y:x+y, a,b))', 'sentence = "Welcome To Beijing!"', 'words = sentence.split()', 'lengths = map(lambda x:len(x),words)', 'list(lengths)', 'def my_range(first=0, last=10, step=1):\n number == first\n while number < last:\n yield number\n number += step\n ', 'my_range', 'ranger = my_range(1, 5)', 'ranger', 'for x in ranger:\n print(x)\n ', 'list(ranger)', 'my_range(2, 4)', 'list(my_range(1, 3))', "get_ipython().run_line_magic('cls', '')", "def document_it(func):\n def new_function(*args, **kwargs):\n print(' 当前运行的函数', func.__name__)\n print('位置参数', args)\n print('关键字参数', kwargs)\n result = func(*args, **kwargs)\n print('Result:', result)\n return result\n return new_function\n ", 'def add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', 'cooler_add_ints = document_it(add_ints)', 'cooler_add_ints(3, 5)', '@document_it\ndef add_ints(a,b):\n return a + b\n ', 'add_ints(3, 5)', 'def square_it(func):\n def new_function(*args, **kwargs):\n result = func(*args, **kwargs)\n return result * result\n return new_function\n ', '@document_it\n@square_it\ndef add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', '@square_it\n@document_it\ndef add_ints(a, b):\n return a + b\n ', 'add_ints(3, 5)', "animal = 'cat", "animal = 'cat'", "def print_global():\n global animal\n animal = 'dog'\n print(animal)\n ", 'animal', 'print_global()', 'animal', "animal = 'fruitbat'", "def print_global():\n print('inside print_global:', animal)\n ", 'print(animal)', 'print_global()', "def change_and_print_global():\n print('inside change_and_print_global:', animal)\n animal = 'wombat'\n print('after the change:', animal)\n ", 'change_and_print_global()', "animal = 'cat'", "def change_and_print_global():\n global animal\n animal = 'dog'\n print('inside change_and_print_global: ' , animal)\n ", 'animal', 'change_and_print_global()', 'animal', "animal = 'cat'", "def change_local():\n animal = 'dog'\n print('local ', locals())\n ", 'animal', 'change_local()', "print('globals: ', globals())"], 'Out': {6: <filter object at 0x0000023A7BFA14E0>, 10: [6, 8, 10, 12], 14: [7, 2, 8], 16: <function my_range at 0x0000023A7C13B9D8>, 18: <generator object my_range at 0x0000023A7DE9EDE0>, 20: [], 21: <generator object my_range at 0x0000023A7C171048>, 26: 8, 28: 8, 30: 8, 33: 64, 35: 64, 39: 'cat', 41: 'dog', 50: 'cat', 52: 'dog', 55: 'cat'}, 'get_ipython': <bound method InteractiveShell.get_ipython of <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x0000023A79B281D0>>, 'exit': <IPython.core.autocall.ExitAutocall object at 0x0000023A7B4967B8>, 'quit': <IPython.core.autocall.ExitAutocall object at 0x0000023A7B4967B8>, '_': 'cat', '__': 'dog', '___': 'cat', '_i': 'change_local()', '_ii': 'animal', '_iii': "def change_local():\n animal = 'dog'\n print('local ', locals())\n ", '_i1': "Names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']", 'Names': ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach'], '_i2': "B_Name= filter(lambda x: x.startswith('B'),Names) ", 'B_Name': <filter object at 0x0000023A7BFA14E0>, '_i3': 'print(B_Name) ', '_i4': 'print(B_Name())', '_i5': 'B_Name()', '_i6': 'B_Name', '_6': <filter object at 0x0000023A7BFA14E0>, '_i7': 'cls', '_i8': 'a = [1,2,3,4]', 'a': [1, 2, 3, 4], '_i9': 'b = [5,6,7,8]', 'b': [5, 6, 7, 8], '_i10': 'list(map(lambda x,y:x+y, a,b))', '_10': [6, 8, 10, 12], '_i11': 'sentence = "Welcome To Beijing!"', 'sentence': 'Welcome To Beijing!', '_i12': 'words = sentence.split()', 'words': ['Welcome', 'To', 'Beijing!'], '_i13': 'lengths = map(lambda x:len(x),words)', 'lengths': <map object at 0x0000023A7D753908>, '_i14': 'list(lengths)', '_14': [7, 2, 8], '_i15': 'def my_range(first=0, last=10, step=1):\n number == first\n while number < last:\n yield number\n number += step\n ', 'my_range': <function my_range at 0x0000023A7C13B9D8>, '_i16': 'my_range', '_16': <function my_range at 0x0000023A7C13B9D8>, '_i17': 'ranger = my_range(1, 5)', 'ranger': <generator object my_range at 0x0000023A7DE9EDE0>, '_i18': 'ranger', '_18': <generator object my_range at 0x0000023A7DE9EDE0>, '_i19': 'for x in ranger:\n print(x)\n ', '_i20': 'list(ranger)', '_20': [], '_i21': 'my_range(2, 4)', '_21': <generator object my_range at 0x0000023A7C171048>, '_i22': 'list(my_range(1, 3))', '_i23': 'cls', '_i24': "def document_it(func):\n def new_function(*args, **kwargs):\n print('当前运行的函数', func.__name__)\n print('位置参数', args)\n print('关键字参数', kwargs)\n result = func(*args, **kwargs)\n print('Result:', result)\n return result\n return new_function\n ", 'document_it': <function document_it at 0x0000023A7D120510>, '_i25': 'def add_ints(a, b):\n return a + b\n ', 'add_ints': <function square_it.<locals>.new_function at 0x0000023A7D710730>, '_i26': 'add_ints(3, 5)', '_26': 8, '_i27': 'cooler_add_ints = document_it(add_ints)', 'cooler_add_ints': <function document_it.<locals>.new_function at 0x0000023A7DDACE18>, '_i28': 'cooler_add_ints(3, 5)', '_28': 8, '_i29': '@document_it\ndef add_ints(a,b):\n return a + b\n ', '_i30': 'add_ints(3, 5)', '_30': 8, '_i31': 'def square_it(func):\n def new_function(*args, **kwargs):\n result = func(*args, **kwargs)\n return result * result\n return new_function\n ', 'square_it': <function square_it at 0x0000023A7D120E18>, '_i32': '@document_it\n@square_it\ndef add_ints(a, b):\n return a + b\n ', '_i33': 'add_ints(3, 5)', '_33': 64, '_i34': '@square_it\n@document_it\ndef add_ints(a, b):\n return a + b\n ', '_i35': 'add_ints(3, 5)', '_35': 64, '_i36': "animal = 'cat", '_i37': "animal = 'cat'", 'animal': 'cat', '_i38': "def print_global():\n global animal\n animal = 'dog'\n print(animal)\n ", 'print_global': <function print_global at 0x0000023A7D3DC620>, '_i39': 'animal', '_39': 'cat', '_i40': 'print_global()', '_i41': 'animal', '_41': 'dog', '_i42': "animal = 'fruitbat'", '_i43': "def print_global():\n print('inside print_global:', animal)\n ", '_i44': 'print(animal)', '_i45': 'print_global()', '_i46': "def change_and_print_global():\n print('inside change_and_print_global:', animal)\n animal = 'wombat'\n print('after the change:', animal)\n ", 'change_and_print_global': <function change_and_print_global at 0x0000023A7D188268>, '_i47': 'change_and_print_global()', '_i48': "animal = 'cat'", '_i49': "def change_and_print_global():\n global animal\n animal = 'dog'\n print('inside change_and_print_global: ' , animal)\n ", '_i50': 'animal', '_50': 'cat', '_i51': 'change_and_print_global()', '_i52': 'animal', '_52': 'dog', '_i53': "animal = 'cat'", '_i54': "def change_local():\n animal = 'dog'\n print('local ', locals())\n ", 'change_local': <function change_local at 0x0000023A7BF86950>, '_i55': 'animal', '_55': 'cat', '_i56': 'change_local()', '_i57': "print('globals: ', globals())"} In [58]:
'''函数 change_local() 的局部命名空间只含有局部变量 animal 。全局命名空间含有全局变量
animal 以及其他一些东西。'''
名称中 _ 和 __ 的用法
以两个下划线 __ 开头和结束的名称都是 Python 的保留用法。因此,在自定义的变量中不
能使用它们。选择这种命名模式是考虑到开发者一般是不会选择它们作为自己的变量的。
例如,一个函数的名称是系统变量 function.__name__ ,它的文档字符串是 function.__
doc__ :
In [58]: def amazing(): ...: '''This is the amazing function. ...: Want to see it again?''' ...: print('This function is named: ', amazing.__name__) ...: print('And its docstring is: ', amazing.__doc__) ...: In [59]: amazing() This function is named: amazing And its docstring is: This is the amazing function. Want to see it again?
如同之前 globals 的输出结果所示,主程序被赋值特殊的名字 __main__
4.11 使用 try 和 except 处理错误
在一些编程语言中,错误是通过特殊的函数返回值指出的,而 Python 使用异常,它是一段
只有错误发生时执行的代码。
之前已经接触到一些有关错误的例子,例如读取列表或者元组的越界位置或者字典中不存
在的键。所以,当你执行可能出错的代码时,需要适当的异常处理程序用于阻止潜在的错
误发生。
在异常可能发生的地方添加异常处理程序,对于用户明确错误是一种好方法。即使不会及
时解决问题,至少会记录运行环境并且停止程序执行。如果发生在某些函数中的异常不能
被立刻捕捉,它会持续,直到被某个调用函数的异常处理程序所捕捉。在你不能提供自己
的异常捕获代码时,Python 会输出错误消息和关于错误发生处的信息,然后终止程序,例
如下面的代码段:
In [61]: short_list = [1, 2, 3] In [62]: position = 5 In [63]: short_list[5] --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-63-781d7b75fdb1> in <module> ----> 1 short_list[5] IndexError: list index out of range
与其让错误随机产生,不如使用 try 和 except 提供错误处理程序:
In [64]: short_list = [1, 2, 3] In [65]: position = 5 In [66]: try: ...: short_list[5] ...: except: ...: print('Need a position between 0 and', len(short_list)-1, ' but got', position) ...: Need a position between 0 and 2 but got 5
在 try 中的代码块会被执行。如果存在错误,就会抛出异常,然后执行 except 中的代码;
否则,跳过 except 块代码。
像前面那样指定一个无参数的 except 适用于任何异常类型。如果可能发生多种类型的异
常,最好是分开进行异常处理。当然,没人强迫你这么做,你可以使一个 except 去捕捉所
有的异常,但是这样的处理方式会比较泛化(类似于直接输出发生了一个错误)。当然也
可以使用任意数量的异常处理程序。
有时需要除了异常类型以外其他的异常细节,可以使用下面的格式获取整个异常对象:
except exceptiontype as name
下面的例子首先会寻找是否有 IndexError ,因为它是由索引一个序列的非法位置抛出的异
常类型。将一个 IndexError 异常赋给变量 err ,把其他的异常赋给变量 other 。示例中会
输出所有存储在 other 中的该对象的异常。
In [67]: short_list = [1, 2, 3] In [68]: while True: ...: value = input('Position [q to quit]? ') ...: if value == 'q': ...: break ...: try: ...: position = int(value) ...: print(short_list[position]) ...: except IndexError as err: ...: print('Bad index: ', position) ...: except Exception as other: ...: print('Something else broke: ', other) ...: Position [q to quit]? 1 2 Position [q to quit]? 2 3 Position [q to quit]? 3 Bad index: 3 Position [q to quit]? 4 Bad index: 4 Position [q to quit]? 5 Bad index: 5 Position [q to quit]? 6 Bad index: 6 Position [q to quit]? two Something else broke: invalid literal for int() with base 10: 'two' Position [q to quit]? q
'''输入 3 会抛出异常 IndexError ;输入 two 会使函数 int() 抛出异常,被第二个 except 所捕获。'''
4.12 编写自己的异常
前面一节讨论了异常处理,但是其中讲到的所有异常(例如 IndexError )都是在 Python 或
者它的标准库中提前定义好的。根据自己的目的可以使用任意的异常类型,同时也可以自
己定义异常类型,用来处理程序中可能会出现的特殊情况。
这里需要定义一个 类 的新对象,这会在第 6 章深入说明。如果你对类不是很
熟悉,可以学完后面的部分再返回这一节。
一个异常是一个类,即类 Exception 的一个子类。现在编写异常 UppercaseException ,在
一个字符串中碰到大写字母会被抛出。
In [69]: class UppercaseException(Exception): ...: pass ...: In [70]: words = ['eeenie', 'meenie', 'miny', 'MO'] In [71]: for word in words: ...: if word.isupper(): ...: raise UppercaseException(word) ...: --------------------------------------------------------------------------- UppercaseException Traceback (most recent call last) <ipython-input-71-1b9f08ace6a3> in <module> 1 for word in words: 2 if word.isupper(): ----> 3 raise UppercaseException(word) 4 UppercaseException: MO
即使没有定义 UppercaseException 的行为(注意到只使用 pass ),也可以通过继承其父类
Exception 在抛出异常时输出错误提示。
你当然能够访问异常对象本身,并且输出它:
浙公网安备 33010602011771号