1. 程式人生 > >python(基礎--函式)

python(基礎--函式)

函式

普通函式
函式是組織好的,可重複使用的,用來實現單一,或相關聯功能的程式碼段。
函式能提高應用的模組性,和程式碼的重複利用率。
規則:
以 def 關鍵詞開頭,後接函式識別符號名稱和圓括號 ()。
任何傳入引數和自變數必須放在圓括號中間,圓括號之間可以用於定義引數。(引數可以沒有)
函式的第一行語句可以選擇性地使用文件字串—用於存放函式說明。
函式名所有都小寫
函式內容以冒號起始,並且縮排。
return [表示式] 結束函式,選擇性地返回一個值給呼叫方。不帶表示式的return相當於返回 None。
注意:若定義的函式名在程式前面定義的函式名相同則將程式前面的函式名覆蓋
def 函式名(引數列表):
函式體
函式呼叫
你可以通過另一個函式呼叫執行,也可以直接從 Python 命令提示符執行。
引數傳遞
注意:型別屬於物件,變數是沒有型別的
a=[1,2,3]
a=“Runoob”
解析: [1,2,3] 是 List 型別,“Runoob” 是 String 型別,而變數 a 是沒有型別,她僅僅是一個物件的引用(一個指標),可以是指向 List 型別物件,也可以是指向 String 型別物件。
固定引數:
形參:定義函式時,括號裡的引數,沒有具體值
補充:1.形參可以有預設值,呼叫時,該引數可以不賦值,如果賦值就會覆蓋掉預設值
2.如果形參有預設值,那麼該形參後面的引數也必須有預設值
實參:呼叫函式時,括號裡的引數,有具體值
位置引數:函式呼叫時實參的順序是和形參一一對應的,那麼就是位置引數
預設引數:有預設值的引數(預設引數必須放在最後面否則報錯SyntaxError: non-default argument follows default argument)
關鍵字引數:實參賦值時,如果標明引數名字=值,那麼該引數就成為了關鍵字引數,不需要按照順序賦值
非固定引數:
如果定義引數時,引數個數不固定,可以定義非固定引數,一般寫*args
注意:函式引數賦值時,如果非固定引數後還有引數,那麼該引數的賦值必須用關鍵字引數賦值
1.加了星號 * 的引數會以元組(tuple)的形式匯入,存放所有未命名的變數引數。我們也可以不向函式傳遞未命名的變數,如果在函式呼叫時沒有指定引數,它就是一個空元組。
def printinfo( arg1, *vartuple ):
“列印任何傳入的引數”
print ("輸出: ")
print (arg1)
print (vartuple)

呼叫printinfo 函式

printinfo( 70, 60, 50 )
輸出:
70
(60, 50)
2. 加了兩個星號 ** 的引數會以字典的形式匯入。
def printinfo( arg1, **vardict ):
“列印任何傳入的引數”
print ("輸出: ")
print (arg1)
print (vardict)

呼叫printinfo 函式

printinfo(1, a=2,b=3)
輸出:
1
{‘a’: 2, ‘b’: 3}
3. 宣告函式時,引數中星號 * 可以單獨出現,如果單獨出現星號 ,* 後的引數必須用關鍵字傳入。
可更改(mutable)與不可更改(immutable)物件
不可變型別:類似 c++ 的值傳遞,如 整數、字串、元組。如fun(a),傳遞的只是a的值,沒有影響a物件本身。比如在 fun(a)內部修改 a 的值,只是修改另一個複製的物件,不會影響 a 本身。
可變型別:類似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改後fun外部的la也會受影響
嚴格意義我們不能說值傳遞還是引用傳遞,我們應該說傳不可變物件和傳可變物件。
匿名函式
定義:使用 lambda 來建立匿名函式。不再使用 def 語句這樣標準的形式定義一個函式。
規則:
1.lambda 只是一個表示式,函式體比 def 簡單很多。
2.lambda的主體是一個表示式,而不是一個程式碼塊。僅僅能在lambda表示式中封裝有限的邏輯進去。
3.lambda 函式擁有自己的名稱空間,且不能訪問自己引數列表之外或全域性名稱空間裡的引數。
補充:雖然lambda函式看起來只能寫一行,卻不等同於C或C++的行內函數,後者的目的是呼叫小函式時不佔用棧記憶體從而增加執行效率。
lambda [arg1 [,arg2,…argn]]:expression
可以使用"關鍵字引數"進行引數傳遞
g= lambda x,y : x2+y

2

g(y=3,x=2)
13
變數作用域
Python的作用域一共有4種,分別是:
L (Local) 區域性作用域
E (Enclosing) 閉包函式外的函式中
G (Global) 全域性作用域
B (Built-in) 內建作用域
以 L –> E –> G –>B 的規則查詢,即:在區域性找不到,便會去區域性外的區域性找(例如閉包),再找不到就會去全域性找,再者去內建中找。

x = int(2.9) # 內建作用域
g_count = 0 # 全域性作用域
def outer():
o_count = 1 # 閉包函式外的函式中
def inner():
i_count = 2 # 區域性作用域
內建作用域
內建作用域是通過一個名為builtin的標準模組來實現的,但是這個變數名自身並沒有放入內建作用域內,所以必須匯入這個檔案才能夠使用它。在Python3.0中,可以使用以下的程式碼來檢視到底預定義了哪些變數:
import builtins
dir(builtins)
全域性變數,區域性變數舉例
def changeme(mylist):
# “修改傳入的列表”
mylist.append([1, 2, 3, 4]) # 這裡沒有定義,也沒有報錯,說明milist以已經存在,修改的是全域性變數
print("函式內取值1: ", mylist)
print(“2”, id(mylist))
# mylist.clear()
mylist = [9,8,7,6,] # 這裡定義的是區域性變數,和函式外定義的同名列表的id是不一樣的,一個是區域性變數一個是全域性變數,以下在修改是區域性變數,和全域性變數沒有關係。對可更改型別的引用進行修改,結果就不一樣了。
print("函式內取值2: ", mylist)
print(“3”, id(mylist))
return mylist

呼叫changeme函式

mylist = [10, 20, 30]
print(“1”, id(mylist))
print(“4”, id(changeme(mylist)))
print("函式外取值: ", mylist)
print(“5”, id(mylist))

global 和 nonlocal關鍵字
global:當內部作用域想修改外部作用域的變數時,就要用到global和nonlocal關鍵字了。

Nonlocal:如果要修改巢狀作用域(enclosing 作用域,外層非全域性作用域)中的變數則需要 nonlocal 關鍵字了(在二層函式中修改一層函式的區域性變數)

閉包函式
閉包:
在一個外函式中定義了一個內函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用。這樣就構成了一個閉包。
1 外函式返回了內函式的引用:
當我們在python中定義一個函式def demo(): 的時候,記憶體當中會開闢一些空間,存下這個函式的程式碼、內部的區域性變數等等。這個demo只不過是一個變數名字,它裡面存了這個函式所在位置的引用而已。我們還可以進行x = demo, y = demo, 這樣的操作就相當於,把demo裡存的東西賦值給x和y,這樣x 和y 都指向了demo函式所在的引用,在這之後我們可以用x() 或者 y() 來呼叫我們自己建立的demo() ,呼叫的實際上根本就是一個函式,x、y和demo三個變數名存了同一個函式的引用。
同時我們發現,一個函式,如果函式名後緊跟一對括號,相當於現在我就要呼叫這個函式,如果不跟括號,相當於只是一個函式的名字,裡面存了函式所在位置的引用。
2 外函式把臨時變數繫結給內函式:
一般情況下,在我們認知當中,如果一個函式結束,函式的內部所有東西都會釋放掉,還給記憶體,區域性變數都會消失。但是閉包是一種特殊情況,如果外函式在結束的時候發現有自己的臨時變數將來會在內部函式中用到,就把這個臨時變數繫結給了內部函式,然後自己再結束。
Python中一切都是物件,雖然函式我們只定義了一次,但是外函式在執行的時候,實際上是按照裡面程式碼執行的,外函式裡建立了一個函式,我們每次呼叫外函式,它都建立一個內函式,雖然程式碼一樣,但是卻建立了不同的物件,並且把每次傳入的臨時變數數值繫結給內函式,再把內函式引用返回。雖然內函式程式碼是一樣的,但其實,我們每次呼叫外函式,都返回不同的例項物件的引用,他們的功能是一樣的,但是它們實際上不是同一個函式物件。
Eg1
def outer(x):
a = 10
def inner(y):
nonlocal x
x = y + x + a
return x
return inner
print(outer(10)(3))
print(outer(20)(5))
print(outer(10)(3))
實行結果:
23
35
23
閉包中內函式修改外函式區域性變數:
1 在python3中,可以用nonlocal 關鍵字宣告 一個變數, 表示這個變數不是區域性變數空間的變數,需要向上一層變數空間找這個變數。
2 在python2中,沒有nonlocal這個關鍵字,我們可以把閉包變數改成可變型別資料進行修改,比如列表。
還有一點需要注意:使用閉包的過程中,一旦外函式被呼叫一次返回了內函式的引用,雖然每次呼叫內函式,是開啟一個函式執行過後消亡,但是閉包變數實際上只有一份,每次開啟內函式都在使用同一份閉包變數
Eg2
def outer(x):
a = 10
def inner(y):
nonlocal x
x = y + x + a
return x
return inner

a = outer(10)
print(a(1))
print(a(3))
print(a(3))
實行結果:
21
34
47
註解:a = outer(10)這行程式碼是將外部函式的x賦值並繫結到內部函式中,並將內部函式的引用賦值給a
print(a(1))是直接呼叫內部函式,引數1是對應定義內部函式形參y的值
因為x是外部函式的形參,繫結到內部函式中,並且內部函式中使用了nonlocal,所以x是可以更改的,閉包函式每次呼叫都是使用同一份閉包變數,所以x的值不斷增加(都是使用a進行呼叫閉包函式)。所以,a(3)和a(3)的結果不一樣。Eg2中外部函式每次都會重新進行例項化,所以不會被重複呼叫