python基礎教程(第三版)學習筆記(六)
第六章 抽象(函式)
6.1 懶惰是一種美德
6.2 抽象和結構
6.3 自定義函式
判斷某個物件是否可呼叫,可使用內建函式callable。格式是callable(物件)。
函式是結構化程式設計的核心。使用def(表示定義函式)語句。
'''
def fun(str_s): #定義函式
return str_s #函式返回內容
f=fun("你好") #呼叫函式
print(f)
'''
你好
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
6.3.1 給函式編寫文件要給函式編寫文件,以確保其他人能夠理解,可添加註釋(以#打頭的內容)。還有另一種編寫註釋的方式,就是新增獨立的字串。在有些地方,如def語句後面(以及模組和類的開頭),新增這樣的字串很有用。放在函式開頭的字串稱為文件字串(docstring),將作為函式的一部分儲存起來。
'''
def fun(a):
"這是一個簡單的輸入字元的函式"
return a*a
helps=fun.__doc__
print(helps)
'''
這是一個簡單的輸入字元的函式
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
也可以用三重雙(單)引號定義函式文件。
'''
def fun_(str_s): ''' "這是一個簡單的函式" ''' return str_s fu=fun_("你好嗎?") fuc=fun_.__doc__ print (fu,"\n",fuc)
'''
這是一個簡單的輸入字元的函式
你好嗎?
"這是一個簡單的函式"
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
特殊的內建函式help很有用。在互動式直譯器中,可使用它獲取有關函式的資訊,其中包含函式的文件字串。
'''
print(help(fun_))
'''
'more' 不是內部或外部命令,也不是可執行的程式
或批處理檔案。
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
注:如果出現這種情況,把C:/windows/system32新增到系統環境變數的path中。
6.3.2 其實函式並不是數學意義上的函式
數學意義上的函式始終有確定的返回值,而這裡的函式即使有return語句也可能相當於break只是結束函式,而break是結束迴圈而已。
'''
def function_():
print("你好")
return
print("很好!")
function_()
'''
你好
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
這是一個熟悉的值:None。由此可知,所有的函式都返回值。如果你沒有告訴它們該返回什麼,將返回None。
6.4 引數魔法
6.4.1 值從哪裡來
編寫函式旨在為當前程式(甚至其他程式)提供服務,確保它在提供的引數正確時完成任務,並在引數不對時以顯而易見的方式失敗。
6.4.2 引數能修改嗎?
定義函式是的引數名和要傳遞的引數名是兩碼事。定義的函式引數叫形參,要傳遞的引數叫實參。實參可以是常量、變數、表示式、函式等,無論實參是何種型別的量,在進行函式呼叫時,它們都必須具有確定的值,以便把這些值傳送給形參。 因此應預先用賦值,輸入等辦法使實參獲得確定值。在函式內部重新關聯引數(即給它賦值)時,函式外部的變數不受影響。
6.4.3 關鍵字引數和預設值
沒有預設值的引數是位置引數。
'''
def f1(n,m):
print("{},{}".format(n,m))
def f2(m,n):
print("{},{}".format(m,n))
print(f1("你","好"))
print(f2("你","好"))
'''
你,好
None
你,好
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
引數的排列順序可能難以記住,尤其是引數很多時。為了簡化呼叫工作,可指定引數的名稱。
'''
def score(name="王五",va=80):
print("{}的成績是:{}分".format(name,va))
print(score())
'''
王五的成績是:80分
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
其中王五是name的預設值,80是va的預設值,而這種由預設值的引數。
'''
print(score("李四"))
'''
李四的成績是:80分
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
'''
print(score("李四",90))
'''
李四的成績是:90分
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
'''
print(score(va=90))
'''
王五的成績是:90分
None
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
注意:如果python的函式可以用=直接給引數賦值。如果第一個關鍵字引數不賦值在python中用,號區隔會出現錯誤,只能用=號直接賦值。
6.4.4 收集引數
在形參前加*號可以允許輸入任意多個實參。
'''
def number(*n):
print(n)
number(1,2,3,4,5,6)
'''
(1, 2, 3, 4, 5, 6)
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
引數前面的星號將提供的所有值都放在一個元組中,也就是將這些值收集起來。這樣的行為我們在5.2.1節見過:賦值時帶星號的變數收集多餘的值。它收集的是列表而不是元組中多餘的值,但除此之外,這兩種用法很像。
'''
def dic(**d):
return d
print(dic())
'''
{}
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
引數前面的雙星號將提供的所有值都放在一個字典中,這種函式的實參要求同時傳入引數名稱和值。
'''
print(dic(name="許九",age=50))
'''
{'name': '許九', 'age': 50}
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
這種引數叫關鍵字引數,(有些書籍把帶有預設值引數的位置引數也叫做關鍵字引數)。
6.4.5 分配引數
在呼叫函式時的實參前面加*號。
'''
def sum(x,y):
return (x+y)
s=[1,4]
print(sum(*s))
'''
5
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
'''
d={"ID":2018,"name":"張三","age":60}
print(dic(**d))
'''
{'ID': 2018, 'name': '張三', 'age': 60}
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
在python裡還有一種引數,限制關鍵字引數,它要求實參不但要有名稱和值而且關鍵字是指定的。
'''
def f(*,name,age):
return name+"的年齡是"+str(age)+"歲。"
print(f(name="王五",age=25))
'''
王五的年齡是25歲。
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
PEP 8(https://www.python.org/dev/peps/pep-0008/)建議程式碼行的長度不要超過79字元,這樣只要編輯器視窗適中,就能看到整行程式碼。如果形參很多,導致函式定義的長度超過了79字元,可在函式定義中輸入左括號後按回車鍵,並在下一行按兩次Tab鍵,從而將形參列表和只縮排一層的函式體區分開來。
'''
def fnn(
numb1,numb2,
numb3,numb4,
numb5,numb6):
"""
這是一個空函式,用於測試多引數函式的書寫方法,是否正確。
"""
pass
fnn(1,2,3,4,5,6)
help(fnn)
'''
Help on function fnn in module __main__:
fnn(numb1, numb2, numb3, numb4, numb5, numb6)
這是一個空函式,用於測試多引數函式的書寫方法,是否正確。
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
6.5 作用域
變數的有效範圍就是變數的作用域。在python中分為全域性作用域和函式作用域。在函式外部定義的變數的作用域較全域性作用域;在函式內部定義的變數叫函式作用域,又叫區域性作用域。python中沒有塊作用域。
'''
v=5
if v!=5: #全域性變數
a=2
pass
def fn():
fnv=3 #函式變數
print(v,fnv)
print(locals())
print(v)
fn()
print(locals())
'''
5
5 3
{'fnv': 3}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_f
rozen_importlib_external.SourceFileLoader object at 0x0000000001D6DA58>, '__spec
__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>
, '__file__': 'xx.py', '__cached__': None, 'v': 5, 'fn': <function fn at 0x000
0000001D2C1E0>}
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
由此可以看出,fn中的變數只有一個fnv,在全域性中的全域性變數是v。而locals所指示的是變數的儲存空間,又叫名稱空間,它是一個字典。作用域和名稱空間有著密切關係,但由此可以看出,名稱空間和作用域是有區別的,作用域是指的有效範圍,而名稱空間是指地所在的字典(空間),一個物件的作用域只有一個,而名稱空間不是,對於變數,每呼叫一次變數所在的函式一次,就會產生一個名稱空間,函式結束,這個空間瞬即消亡。
讀取全域性變數的值通常不會有問題,但還是存在出現問題的可能性。如果有一個區域性變數或引數與你要訪問的全域性變數同名,就無法直接訪問全域性變數,因為它被區域性變數遮住了。
如果需要,可使用函式globals來訪問全域性變數。這個函式類似於vars,返回一個包含全域性變數的字典。
'''
w=10
def fn1():
w=7
print("函式外部變數w",globals()['w'])
print("函式內部變數w",w)
fn1()
'''
函式外部變數w 10
函式內部變數w 7
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
在函式中為全域性變數賦值,用global明示該變數為全域性變數。
'''
w=10
def fn1():
global w
w=7
print("函式外部變數w",globals()['w'])
print("函式內部變數w",w)
fn1()
'''
函式外部變數w 7
函式內部變數w 7
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
另外,還有一個關鍵字nonlocal用它可以在內部函式內給外部函式的變數賦值。正像用global給全域性變數賦值那樣。
6.6 遞迴
遞迴函式就是函式呼叫自身並返回。
6.6.1 兩個經典案例:階乘和冪
階乘:n*(n-1)*(n-2)...*2*1
'''
def fact(n): #n的階乘
if n==1: #如果n=1
return 1 #則返回1
else: #否則
return n*fact(n-1) #n*(n-1)
print(fact(10))
'''
3628800
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
冪:x^y即y個x相乘。
'''
def powe(x,y): #y個x相乘
if y==0: #如果y等於零
return 1 #就返回一,
else: #否則
return x*powe(x,y-1) #x乘於(y-1)個x
p=powe(2,3)
print(p)
'''
8
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
6.6.2 另一個經典案例:二分查詢
如果上限和下限相同,就說明它們都指向數字所在的位置,因此將這個數字返回。
否則,找出區間的中間位置(上限和下限的平均值),再確定數字在左半部分還是右半部分。然後在繼續在數字所在的那部分中查詢。
def search(sequence, number, lower, upper): #sequence序列、number數、lower下限、upper上限。
if lower == upper: #如果上下限相等(序列僅一個數)
assert number == sequence[upper] #設定斷言:如果查詢的數等於這個數,
return upper #就返回這個位置。
else: #否則
middle = (lower + upper) // 2 #取中間位子
if number > sequence[middle]: #如果查詢的數大於中間位置的數
return search(sequence, number, middle + 1, upper) #就把中間位置作為下限查詢,
else: #否則
return search(sequence, number, lower, middle) #就把中間位置作為上限查詢。
為方便呼叫,還可將上限和下限設定為可選的。為此,只需給引數lower和upper指定預設值,並在函式開頭新增如下條件語句:
def search(sequence, number, lower=0, upper=None):
if upper is None: upper = len(sequence) - 1
'''
def search(sequence, number, lower=0, upper=None):
if upper is None: upper = len(sequence) - 1
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)
seq=[34,67,8,123,4,100,95]
seq.sort()
print(seq)
sea=search(seq,34)
print(sea)
'''
[4, 8, 34, 67, 95, 100, 123]
2
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
實際上,模組bisect提供了標準的二分查詢實現,可以參閱。
附:函數語言程式設計——介紹幾個函式
1、map
map() 會根據提供的函式對指定序列做對映。
第一個引數 function 以引數序列中的每一個元素呼叫 function 函式,返回包含每次 function 函式返回值的新列表。
格式為:map(function, iterable, ...)Python 2.x 返回列表;Python 3.x 返回迭代器。
其中function 為函式,iterable為一個或多個序列
'''
x=[1,2,3,4,5]
y=[9,8,7,6,5]
def lis(x,y):
return x+y
m1=map(lis,x,y)
print(list(m1))
'''
[10, 10, 10, 10, 10]
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
也可以這樣寫:
'''
m2=map(lambda i,j:i+j ,x,y)
print(list(m2))
'''
[10, 10, 10, 10, 10]
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
其中lambda是用來定義匿名函式的表示式。格式為:lambda 引數1,引數2...:函式體
2、filter
filter() 函式用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。
接收兩個引數,第一個為函式,第二個為序列,序列的每個元素作為引數傳遞給函式進行判,然後返回 True 或 False,最後將返回 True 的元素放到新列表中。
格式為filter(function, iterable)
其中function為判斷函式,iterable為可迭代物件。
'''
nums=[2,"d","&&&","a3","..."]
fi=filter(lambda x:str(x).isalnum(),nums)
print(list(fi))
'''
[2, 'd', 'a3']
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
其中isalnum() 方法檢測字串是否由字母和數字組成。如果字串中至少有一個字元並且所有字元都是字母或數字則返回 True,否則返回 False
3、 reduce
reduce() 函式會對引數序列中元素進行累積。
格式為reduce(function, iterable[, initializer])
其中function為函式,有兩個引數;iterable為可迭代物件;initializer為可選,初始引數。
其功能是將一個數據集合(連結串列,元組等)中的所有資料進行下列操作:用傳給 reduce 中的函式 function(有兩個引數)先對集合中的第 1、2 個元素進行操作,得到的結果再與第三個資料用 function 函式運算,最後得到一個結果。
'''
arr=[1,2,3,4,5,6,7,8,9,10] #這裡不能用arr=range(10),不然結果為零。
from functools import reduce
ss=reduce(lambda x,y:x*y,arr) #10的階乘
print(ss)
'''
3628800
------------------
(program exited with code: 0)
請按任意鍵繼續. . .
(待續)