《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
>>>