9.函數詳解-python基礎知識
匿名函數lambda函數將在後續文章中講解,事實上,如果把它看做一個表達式也是可以理解的。
定義,調用
函數通過def
定義,執行時會創建函數對象,並將其賦值給函數名,通過return
返回值,如果執行控制流執行到函數體末尾沒有發現可執行return
的時候,則會返回None
。
-
定義:
def functionname(arg1,...,argn): 函數語句 return 返回值
-
調用:
通過函數名調用
-
多態:
python中的函數是多態的,不必定義傳入參數的類型
作用域
訪問:LGBE原則
- L: local,局部作用域,即函數,類中定義的變量。
- E: enclosing,父級函數的局部作用域,即此函數的上級函數的局部作用域。
- G: globa,全局作用域
- B: build-in,內置作用域,如:int,max函數等,使用
builtins
函數查看全部
訪問優先級順序爲:局部作用域(L)>父級函數作用域(E)>全局作用域(G)>系統模塊(B)
來看一個例子:
x = 99
def func1(y):
z = x+y
return z
print(func1(1))
>>>100
x = 99
def func2():
x = 100
func2()
print(x)
>>>99
前面說過,由於函數內的參數在函數結束時會被回收,再func1中,z是在函數中定義的,y是傳入的參數,x先訪問局部作用域沒找到,在訪問到全局作用域,找到了x=99,然後計算返回了z
而在func2中,函數內的x=100不是對全局變量x進行了賦值更改,而是重新定義了局部作用域的變量x,所以在全局作用域輸出x還是等於100,要實現賦值變更,需要用到下面講到的global
函數
global,nonlocal
global
將變量聲明爲全局變量,在整個模塊文件中都可以使用,global需要在函數內部聲明,在外部聲明則無效
#在局部作用域func1中調用全局變量x
x = 99
def fun1():
global x
x = 100
fun1()
print(x)
>>>100
nonlocal
將變量聲明爲父級函數的局部變量
x = 99
def fun1():
global x#聲明x是全局變量的x
x = 100#實現了對x的賦值修改
y = 100#聲明fun1的局部變量y
def fun2():
nonlocal y#聲明y是父級fun1的局部變量
y = 99#實現對y的修改
print('func2,y:',y)
fun2()
fun1()
print('main,x:',x)
>>> func2,y: 99
main,x: 100
如果父級沒有定義該變量,則會報錯,即使父級引入全局變量也不行:
x = 99
y = 100
def fun1():
global x ,y
x = 100
# y = 100 #父級未定義y
def fun2():
nonlocal y #報錯,y未定義
y = 99
print('func2,y:',y)
fun2()
fun1()
print('main,x:',x)
>>> File "E:/Python_project/Django/QShop/QShop/__init__.py", line 7
nonlocal y
^
SyntaxError: no binding for nonlocal 'y' found
*循環變量與默認參數
默認參數x=x,這樣既完成了從父級循環導入變量,又不會像global一樣改變原有變量值:
def fun1():
x=10
def fun2(x=x):
x=20
print('fun2',x)
fun2()
print('fun1',x)
fun1()
再來看一個例子,如果需要使用循環來定義一些嵌套函數,需要使用默認參數來保存狀態:
lambda也是定義了函數,函數makeAction可以用來計算次方
def makeActions():
acts = []
for i in range(5):
acts.append(lambda x:i**x)
return acts
acts = makeActions()
print(acts[0])
>>><function makeActions.<locals>.<lambda> at 0x000001E56F172AF8>
但是上面的函數,傳入底數2
acts[0](2) >>>16
acts[1](2) >>>16
acts[2](2) >>>16
acts[3](2) >>>16
acts[4](2) >>>16
所有的都是2的四次方,說明參數i都是4,這時就需要i保持默認值:
def makeActions():
acts = []
for i in range(5):
acts.append(lambda x,i=i:i**x)
return acts
acts = makeActions()
acts[0](2) >>>0
acts[1](2) >>>1
acts[2](2) >>>4
acts[3](2) >>>9
acts[4](2) >>>16
參數
如何傳遞
參數實際上傳遞的引用,將對象地址指向函數運行時新建的變量上
可變對象此時對其操作會改變對象,所有引用對象的變量會改變值
l = [1,2,3]
def change(object):
object = object.append(4)
change(l)
print(l)
>>>[1, 2, 3, 4]
不可變對象的操作其實是新建對象來指向,所以不會改變原對象的值
如果仔細閱讀過前面的可變對象與不可變對象的原理,這裏是很容易理解的。
s = 'abc'
def change(object):
object = object+'de'
change(s)
print(s)
>>> abc
參數類型
-
匹配規則:從左往右進行匹配,常用參數必須放在前面
1.通過位置分配常用參數
2.通過匹配變量名分配關鍵字參數
3.其他額外的非關鍵字參數匹配到*name元組中
4.其他額外的關鍵字參數分配到**name字典中 -
常用參數:
def f(a,b,c): print(a,b,c) f(1,2,3)>>>1 2 3
-
默認參數:爲沒有傳入值的參數定義缺省值
在函數定義時執行def f(a=1,b=2,c=3): print(a,b,c) f(1)>>>1 2 3
-
關鍵字參數:通過參數名進行匹配
在函數調用時使用def f(a,b,c): print(a,b,c) name = 'x';age = 14;city = 'BJ' f(a=age,b=city,c=name)>>>14 BJ x
-
收集參數:在函數定義中,使用
*
用元組收集任意多基於位置的參數,使用**
用字典收集任意多關鍵字參數,並且收集參數是可以一起使用的。def f(a,b,c,*d): print(a,b,c,d) f(1,2,3,4,5,6,7) >>>1 2 3 (4, 5, 6, 7)
def f(a,b,**d): print(a,b,d) f(1,2,x=3,y=4) >>>1 2 {'x': 3, 'y': 4}
-
可變解包參數:與收集參數剛好相反,解包參數是在函數調用時,使用
*
解包基於位置的參數,使用**
解包關鍵字參數。def f(a,c,x,y): print(x,y,a,c) D={'x': 3, 'y': 4} f(*(1,2),**D) >>>3 4 1 2
再次需要我們注意的是,不管是函數定義還是參數解包賦值的時候,一定要注意參數的順序,常用參數放前面,關鍵字參數放後面,不然會匹配不到常用參數報錯。
-
keyword-only參數:
在一個星號參數後、或者一個可變位置參數後的形參
函數調用時必須使用關鍵字參數傳參
調用時如果沒有默認值,則必須傳遞實參,否則將拋出TypeError缺少keyword-only參數異常def func(*args, x=1, y, **kwargs): print(x) print(y) print(args) print(kwargs) func(3, 5, x=3, y=5, b='KeithTt')