1. 程式人生 > >python基礎教程(第三版)學習筆記(六)

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)

請按任意鍵繼續. . .

(待續)