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')
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章