《Python 快速入门(第3版)》娜奥米·赛德
8.2 if-elif-else语句
Python中没有提供case语句。在大多数其它语言采用case或switch语句的场合,Python可以用串联的if...elif...elif...else结构来应对。如果遇到极少数棘手的场合,通常可用函数字典来解决
def do_a_stuff():
#process a
def do_b_stuff():
#process b
def do_c_stuff():
#process c
func_dict = {'a' : do_a_stuff,
'b' : do_b_stuff,
'c' : do_c_stuff }
x = 'a'
func_dict[x]()
例如,
def do_a_stuff(a,b):
return a+b
def do_b_stuff(a,b):
return a-b
def do_c_stuff(a,b):
return a*b
func_dict = {'a' : do_a_stuff,
'b' : do_b_stuff,
'c' : do_c_stuff }
x = 'a'
y = func_dict[x](333,555)
print(y) #输出888
8.3.1 range函数
对于给出的数字n,range(n)会返回0、1、2、……、n-2、n-1。因此,将列表长度(调用len函数)传入就会产生列表元素索引的序列。
>>> range(10)
range(0, 10)
>>> type(range(10))
<class 'range'>
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
如果有两个数值参数,则第一个是结果序列的起始值,第二个是结果序列的结束值(不含)。
range函数的第三个可选参数,以便给出计数时的步进值。
>>> list(range(2,8))
[2, 3, 4, 5, 6, 7]
>>> list(range(2,8,3))
[2, 5]
>>>
8.3.4 for循环和元组拆包
下述代码在for关键字之后紧跟着用到了元组x,y,而不是平常的单个变量。
>>> somelist = [(1, 2), (3, 7), (9, 5)]
>>> result = 0
>>> for x, y in somelist:
... result = result + (x * y)
...
>>> result
68
>>>
8.3.5 enumerate函数
enumerate函数返回的是元组(索引,数据项)
通过组合使用元组拆包和enumerate函数,可以实现同时对数据项及其索引进行循环遍历。
>>> x = [2, -3, 5, -7, -11]
>>> enumerate(x)
<enumerate object at 0x7f752b4ed090>
>>> list(enumerate(x))
[(0, 2), (1, -3), (2, 5), (3, -7), (4, -11)]
>>>
>>> for i,n in enumerate(x):
... if n < 0:
... print("Found a negative number at index:", i)
...
Found a negative number at index: 1
Found a negative number at index: 3
Found a negative number at index: 4
>>>
8.3.5 zip函数
zip函数可以从一个或多个可迭代对象中逐一读取对应元素,并合并为元组,直至长度最短的那个可迭代对象读取完毕
>>> a = [1, 2, 3, 4]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f']
>>> z = zip(a,b)
>>> list(z)
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
>>>
8.4 列表和字典推导式
列表推导式的用法如下:
new_list = [expression1 for variable in old_list if expression2]
字典推导式的用法如下:
new_dict = {expression1:expression2 for variable in list if expression3}
生成器表达式
注意:除方括号的变化之外,生成器表达式还不返回列表。生成器表达式返回的是生成器对象,可用作for循环中的迭代器,这与range()函数的用途非常类似。
使用生成器表达式的优点是,不会在内存中生成整个列表。因此可生成任意大小的值序列,内存开销也很小。
动手题:推导式为了去除列表x中的负数,该用什么列表推导式来处理列表?
创建能返回1到100之间奇数的生成器。提示:奇数除以2后会有余数,余数可以用%2操作得到。
编写代码创建一个字典,键为11到15之间的数字,值为键的立方。
1.
>>> x_nonegative = [ a for a in x if a >=0 ]
>>> x_nonegative
[11, 666, 5, 223, 886]
>>>
>>>
>>> x_nonegative = [ x for x in x if x >=0 ]
>>> x_nonegative
[11, 666, 5, 223, 886]
>>>
2.
>>> gen = (i for i in list(range(100)) if i % 2 !=0)
>>> next(gen)
1
>>> next(gen)
3
>>> next(gen)
5
>>> next(gen)
7
>>>
3.
>>> { key:key**3 for key in list(range(11,16))}
{11: 1331, 12: 1728, 13: 2197, 14: 2744, 15: 3375}
>>>
8.6 布尔值和布尔表达式
操作符and和or将会返回对象
and操作符要么返回第一个为False的对象(表达式的计算结果),要么返回最后一个对象。
or操作符要么返回第一个为True的对象,要么返回最后一个对象。
大多数情况下,用到的都是==和!=,而不是is和is not。is和is not用来判断操作对象是否为同一个对象
9.4 局部变量、非局部变量和全局变量
nonlocal语句与global语句类似,它会让标识符引用最近的闭合作用域(enclosing scope)中已绑定的变量。
例:
如果想对函数之外的变量赋值,就必须将其显式声明为nonlocal或global。但如果只是要访问函数外的变量,则不需要将其声明为nonlocal或global。
9.6 lambda表达式
lambda parameter1, parameter2, . . .: expression
>>> deg_C2K = lambda deg_C: 273.15 + deg_C
>>> deg_C2K(32)
305.15
>>>
9.8 装饰器
def decorate(func):
print("in decorate function, decorating", func.__name__)
def wrapper_func(*args):
print("Executing", func.__name__)
return func(*args)
return wrapper_func
def myfunction(parameter):
print(parameter)
myfunction = decorate(myfunction)
myfunction("hello")
输出结果:
in decorate function, decorating myfunction
Executing myfunction
hello
装饰器(decorator)就是上述过程的语法糖(syntactic sugar),只增加一行代码就可以将一个函数包装到另一个函数中去。效果与上述代码相同。
装饰器由两部分组成:先定义用于包装或“装饰”其他函数的装饰器函数;然后立即在被包装函数的定义前面,加上“@”和装饰器函数名。
def decorate(func):
print("in decorate function, decorating", func.__name__)
def wrapper_func(*args):
print("Executing", func.__name__)
return func(*args)
return wrapper_func
@decorate
def myfunction(parameter):
print(parameter)
myfunction("hello")
输出结果:
in decorate function, decorating myfunction
Executing myfunction
hello
交互模式
>>> def decorate(func):
... print("in decorate function, decorating", func.__name__)
... def wrapper_func(*args):
... print("Executing", func.__name__)
... return func(*args)
... return wrapper_func
...
>>> @decorate
... def myfunction(parameter):
... print(parameter)
...
in decorate function, decorating myfunction
>>>
>>> myfunction("hello")
Executing myfunction
hello
>>>
10.2 编写第一个模块
mymath.py内容如下
"""mymath - our example math module"""
pi = 3.14159
def area(r):
global pi
return(pi * r * r)
>>> import mymath
>>> pi
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'pi' is not defined
>>>
>>> mymath.pi
3.14159
>>>
如果修改了磁盘中的模块文件,那么再次输入import命令并不会重新加载模块,这时要用到模块importlib的reload函数才可以。importlib模块为访问模块导入的后台机制提供了一个接口:
>>> mymath.pi
3.14159
>>> import mymath, importlib
>>> importlib.reload(mymath)
<module 'mymath' from '/root/mymath.py'>
>>> mymath.pi
3.14
>>>
当模块被重新加载(或第一次导入)时,其所有的代码都会被解析一遍。如果发现错误,则会引发语法异常。反之,如果一切正常就会创建包含Python字节码的.pyc文件,如mymath.pyc。
10.4 模块搜索路径
sys.path是Python的搜索模块的路径集,是一个列表
>>> sys.path
['', '/usr/local/python3/lib/python39.zip', '/usr/local/python3/lib/python3.9', '/usr/local/python3/lib/python3.9/lib-dynload', '/usr/local/python3/lib/python3.9/site-packages']
>>>
说明:sys.path的第一个元素为"",这会告知Python要在当前目录中查找模块。
10.5 模块内部私有名称
模块中下划线开头的标识符不能用from module import *导入。用前导下划线表示私有名称的约定,整个Python中都在使用,而不仅用在模块中。
例如 modtest.py内容如下
def f(x):
return x
def _g(x):
return x
a = 4
_b = 2
启动一个交互式会话测试
>>> from modtest import *
>>> f(3)
3
>>> _g(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_g' is not defined
>>>
注意:只有from <module> import *才会有这种行为。以下方式操作还是可以访问到_g和_b的:
启动一个交互式会话测试
>>> import modtest
>>> modtest._g(3)
3
>>> modtest._b
2
>>>
启动一个交互式会话测试
>>> from modtest import _g,_b
>>> _g(3)
3
>>> _b
2
>>>