Python/函數
函數定義
[編輯]Python函數定義的通常格式為:
def 函数名(参数列表):
#函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明
函数体
#return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None
參數的傳值與傳引用
[編輯]Python中,對象有類型;變量僅僅是對象的一個引用,變量沒有類型。 string、tuple和 numbers 是不可更改的類型(immutable type),給它們的變量賦值其實是指向了新的對象;而 list,dict 等則是可以修改的類型(mutable type)。Python函數的參數傳遞,在函數內部最初都是訪問實參那個對象而不是複製一份:
- 不可變類型:在函數內部「修改」參數的值,只是引用到另一個對象,不影響實參本身。
- 可變類型:在函數內部修改參數的值後,函數外部的實參也會受影響
命名實參與不定長參數
[編輯]Python的函數實參可分為兩類:
- 無命名的非關鍵字實參(non-keyword argument)或位置實參(positional argument)
- 關鍵字實參(keyword argument)或譯作命名實參。關鍵字實參之間的順序可以任意。
關鍵字實參必須出現在非關鍵字實參右側,否則報錯「positional argument follows keyword argument」。
函數形參可分為
- 無缺省值的形參
- 有缺省值的形參
*args
**kwargs
,稱為「var-keyword argument」。*
(單個星號)/
(單個斜槓)
def foo(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): ------------- ------------- ------------- | | | | Positional or keyword | | - Keyword only -- Positional only
def bar(param1, param2=default_value,*args, kwd1, **kwargs):
函數的形參表必須遵守下屬規則:
- 出現在
*args
或者*
之後的形參都只能對應命名實參 - 帶缺省值的形參,其右側一直到
*
的所有參數都必須是有缺省值形參。參數的缺省值在函數定義時從左至右求值。 - 形參中沒有名字的單獨一個星號
*
,只是起到分隔符作用,並不對應於實參,表示實參表此處向右都必須是命名實參。這適用於當名稱有意義並且函數定義通過顯式名稱更容易理解時,只用關鍵字實參;或者您希望防止用戶依賴所傳遞參數的位置。 - 從Python 3.8開始,形參中沒有名字的單獨一個斜槓
/
,只是起到分隔符作用,並不對應於實參,表示實參表此處向左都必須是位置形參。這適用於調用函數時強制實參的求值順序;或者以後修改形參名字但不影響調用者使用。「僅是位置形參」(Positional only)的名字將不再嘗試與關鍵字實參的名字匹配,這意味着同名的關鍵字實參可以被**kwargs
收集,這稱為Recap。 - 形參
**kwargs
必須出現在*args
的右側,否則報錯「invalid syntax」 - 形參
**kwargs
必須是最後一個形參,否則報錯「arguments cannot follow var-keyword argument」 - 通常,不在一個函數同時使用
*args
與**kwargs
,以免混淆。
調用函數時,實參可分為四類:
- 位置實參
- 關鍵字實參,形如 name=value
- 單星號解包實參,形如*args
- 雙星號解包實參,形如**kwargs
實參在實參表中出現的規則:
- 實參表可分為左、中、右三部分,任何部分都可以為空
- 實參表左部分,任意數量的位置實參可以和任意數量的單星號解包實參混雜出現;
- 實參表中部分,任意數量的關鍵字實參可以和任意數量的單星號解包實參混雜出現;
- 實參表右部分,任意數量的關鍵字實參可以和任意數量的雙星號解包實參混雜出現。
函數調用時,形實參數結合的規則:
- 首先形參表的「僅位置形參」與位置實參從左向右結合。
- 其次形參表的「僅關鍵字形參」與關鍵字實參實參結合。
- 再次對於「可位置可關鍵字形參」:
- 首先與關鍵字實參匹配。
- 其次對於不能匹配關鍵字實參的「可位置可關鍵字形參」,再與位置實參匹配。
- 最後還剩下未匹配的位置實參組成一個元組收集到*args形參,還剩下未匹配的關鍵字實參組成一個字典收集到**kwargs形參。
形參實參結合時一些語法報錯情形:
- 已經匹配的非關鍵字形參,其對應的實參的名字不能再作為關鍵字實參出現,否則報錯「duplicate value for the same argument」或「got multiple values for argument」。
- 特例:「僅是位置形參」(Positional only)的名字將不再嘗試與關鍵字實參的名字匹配,這意味着同名的關鍵字實參可以被
**kwargs
收集,這稱為Recap。
- 特例:「僅是位置形參」(Positional only)的名字將不再嘗試與關鍵字實參的名字匹配,這意味着同名的關鍵字實參可以被
- 如果沒有
**kwargs
形參,關鍵字實參的名字不出現在形參表中則報錯「an unexpected keyword argument」 - 不能被匹配的形參,將報錯「missing 1 required positional argument」或「missing 1 required keyword-only argument」
def test1(a, b, c=0, *args, **kwargs):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kwargs)
def test2(a, b, c=0, *args, d, **kwargs):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'args=', args, 'kw =', kwargs)
# 定义一个元组和字典用作参数传入
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
test1(*args, **kw)
# a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
test2(*args, **kw)
#a = 1 b = 2 c = 3 d = 99 args= (4,) kw = {'x': '#'}
def test3(a, *args,d, c=0):
print('a =', a, 'c =', c, 'd =', d, 'args=', args)
args=(1,3,5)
test3(-1, d=101, *args) # 这是关键字实参与单星号解包实参混杂情形
#输出为:a = -1 c = 0 d = 101 args= (1, 3, 5)
函數返回多個值
[編輯]返回多個值時自動作為一個tuple。函數調用者可以使用多個變量來接受多個返回值(即tuple中的各個成分)。
嵌套函數
[編輯]嵌套函數(nested function),需要注意:
- 內部函數可以讀寫訪問外部函數的可變(mutable)變量(如 list、dict 等)
- 內部函數可以讀訪問外部函數的不可變(immutable)變量(如 int、str、tuple 等)
- 內部函數使用nonlocal關鍵字,可以讀寫訪問任意層次的外部函數的不可變(immutable)變量
- 內部函數訪問的外部函數的變量,可以定義在內部函數之後。
函數閉包
[編輯]閉包是指嵌套函數作為外部函數的返回值。因為函數是頭等對象,所以函數對象可以作為實參、返回值。嵌套函數可以使用外部函數的變量,即使外部函數已經運行結束了。例如
def adder(outer_argument): # outer function
def adder_inner(inner_argument): # inner function, nested function
return outer_argument + inner_argument # Notice outer_argument
return adder_inner
add5 = adder(5) # a function that adds 5 to its argument
add7 = adder(7) # a function that adds 7 to its argument
print add5(3) # prints 8
print add7(3) # prints 10
匿名函數
[編輯]匿名函數的語法如下:
lambda 可选的形参表:expression
lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。lambda 函數擁有自己的命名空間,可以訪問包含它的作用域中的變量。
def make_incrementor(n): return lambda x: x + n
偏函數
[編輯]對一個函數的部分參數綁定值後,得到的函數為偏函數:
偏函数名 = partial(func, *args, **kwargs)
其中,func 指的是要封裝的原函數,*args 和 **kwargs 分別用於接收非命名實參和命名實參。
from functools import partial
#定义个原函数
def display(name,age):
print("name:",name,"age:",age)
#定义偏函数,其封装了 display() 函数,并为 name 参数设置了默认参数
GaryFun = partial(display,name = 'Gary')
#由于 name 参数已经有默认值,因此调用偏函数时,可以不指定
GaryFun(age = 13) #必须以命名实参方式传入,否则会报错:TypeError: display() got multiple values for argument 'name'
def mod( n, m ):
return n % m
#定义偏函数,并设置参数 n 对应的实参值为 100
mod_by_100 = partial( mod, 100 )
print(mod( 100, 7 ))
print(mod_by_100( 7 ))
eval()與exec()
[編輯]eval(source, globals=None, locals=None, /) exec(source, globals=None, locals=None, /)
expression參數是一個字符串,代表要執行的語句 。該語句受後面兩個字典類型參數 globals 和 locals 的限制,只有在 globals 字典和 locals 字典作用域內的函數和變量才能被執行。
globals參數管控的是一個全局的命名空間,即 expression 可以使用全局命名空間中的函數。如果只是提供了 globals 參數,而沒有提供自定義的 __builtins__,則系統會將當前環境中的 __builtins__ 複製到自己提供的 globals 中,然後才會進行計算;如果連 globals 這個參數都沒有被提供,則使用 Python 的全局命名空間。
locals參數管控的是一個局部的命名空間,和 globals 類似,當它和 globals 中有重複或衝突時,以 locals 的為準。如果 locals 沒有被提供,則默認為 globals。
eval() 執行完會返回結果,而 exec() 執行完不返回結果
map()、filter()、reduce()
[編輯]由於 map() 函數是直接由用 C 語言寫的,運行時不需要通過 Python 解釋器間接調用,並且內部做了諸多優化,所以相比其他方法,此方法的運行效率最高。
reduce() 函數在 Python 3.x 中已經被移除,放入了 functools 模塊,因此在使用該函數之前,需先導入 functools 模塊。
函數註解
[編輯]「函數註解是關於用戶自定義函數使用類型的可選的元信息」。也就是說,函數註解的用途為「函數中的形參和返回值提供類型提示信息」。注意,函數註解沒有任何語法上的意義,只是為函數參數和返回值做註解,並在運行獲取這些註解,僅此而已。換句話說,為函數做的註解,Python不做檢查,不做強制,不做驗證,什麼操作都不做,函數註解對Python解釋器沒任何意義。
def f(ham:str,egg:str='eggs')->str:
pass
print(f.__annotations__)
#输出结果为:{'ham': <class 'str'>, 'egg': <class 'str'>, 'return': <class 'str'>}