python基础函数图_Python基础:函数
一、概述
函數(function)是一個可調用的(callable)對象,它獲取一些(0個或多個)參數,然后執行一段代碼,最后返回一個值給調用者。
在Python中,函數是第一級對象(first-class),因此它具有與其他Python對象完全相同的基本行為特征,如可以被傳遞、可以作為右值進行賦值、可以作為另一個函數的參數或返回值等等。
二、聲明、定義和調用
與C/C++不同的是,Python中的函數不單獨區分 聲明 和 定義,這兩者同時發生在def語句被執行時,因此統一稱為 定義。
def funcname([parameters]):
與其他高級語言類似,Python中的函數必須在定義后才能 調用(即先定義,后調用);同時,在函數定義中允許存在 前向引用(即在函數A的定義中引用了函數B,但函數B在函數A之后才定義)。
# 先定義,后調用
>>> funcA()
Traceback (most recent call last):
File "", line 1, in
NameError: name 'funcA' is not defined
>>> def funcA():
... print 'ok'
...
>>> funcA()
ok
# 前向引用
>>> def funcA():
... print 'in funcA()'
... funcB()
...
>>> def funcB():
... print 'in funcB()'
...
>>> funcA()
in funcA()
in funcB()
# 前向引用(也必須遵守“先定義,后調用”的原則)
>>> def funcA():
... print 'in funcA()'
... funcB()
...
>>> funcA()
in funcA()
Traceback (most recent call last):
File "", line 1, in
File "", line 3, in funcA
NameError: global name 'funcB' is not defined
三、參數
以下討論中,實參 是指調用函數時由調用者傳入的參數,形參 是指函數定義中在內部使用的參數(類似于C/C++中的參數概念)。
1、參數傳遞
Python中對函數參數的傳遞采用 傳引用 的方式,即實參和形參都是引用,它們指向同一個對象實體(換言之,即形參是實參的淺拷貝)。
例如有以下函數:
>>> def changer(a, b): # 函數定義
... a = 2 # 改變形參a
... b[0] = 'spam' # 改變形參b
...
>>> X = 1 # 實參X指向一個整數(不可變對象)
>>> L = [1, 2] # 實參L指向一個列表(可變對象)
>>> changer(X, L) # 函數調用
>>> X # 對形參a的修改,不影響實參X
1
>>> L # 對形參b的修改,影響了實參L
['spam', 2]
上述示例中,函數changer的實參和形參的傳遞關系如下:
綜上可知:
如果參數引用的對象本身是 不可變的,如數值、字符串、元組,則在函數中對形參的修改 不會影響 實參
如果參數引用的對象本身是 可變的,如列表、字典,則在函數中對形參的修改 會影響 實參
2、實參類型
調用函數時,可以指定兩種類型的參數:位置參數(positional argument)和關鍵字參數(keyword argument)(參考 argument)。
1)位置參數
位置參數 又稱為非關鍵字參數(non-keyword argument),這種參數的指定方式有兩種:直接以值的形式 和 以 * 開頭的可迭代對象 (iterable)。
例如,在以下對complex()函數的調用中,3和5都是位置參數:
>>> complex(3, 5)
(3+5j)
>>> complex(*(3, 5))
(3+5j)
2)關鍵字參數
關鍵字參數 的指定方式也有兩種:以 name=value 的形式 和 以 ** 開頭的字典。
例如,在以下對complex()函數的調用中,3和5都是關鍵字參數:
>>> complex(real=3, imag=5)
(3+5j)
>>> complex(**{'real': 3, 'imag': 5})
(3+5j)
3)混合使用
如果在調用函數時,要混合使用位置參數和關鍵字參數,則位置參數必須位于關鍵字參數之前。
例如,在以下對complex()函數的調用中,3是位置參數,5是關鍵字參數:
>>> complex(3, imag=5)
(3+5j)
3、形參綁定
在函數定義中,可以指定四種類型的參數:常規參數、默認參數、變長元組參數和變長字典參數。這四種形參類型的區別與 形參綁定 強相關。
形參綁定 是指:調用函數時,Python對實參與形參進行一一匹配的過程(進而完成參數傳遞)。在這個綁定過程中,每種形參能夠接受的實參類型是不同的,具體對應關系如下:
形參類型實參類型(位置參數)實參類型(關鍵字參數)
常規參數
√
√
默認參數
√
√
變長元組參數
√
×
變長字典參數
×
√
下面結合實參與形參的綁定過程,分別介紹形參的這四種類型:
1)常規參數
常規參數 是必須指定的形參。根據實參類型的不同,綁定規則如下:
如果實參是“位置參數”,則按照 參數位置 來嚴格匹配實參和形參,實參和形參的個數必須相等、順序必須一致。
如果實參是“關鍵字參數”,則按照 參數名稱 來嚴格匹配實參和形參,實參和形參的個數必須相等、名稱必須一致(順序不重要)。
參考以下示例:
# 函數定義
>>> def func(a, b):
... print type(a), a
... print type(b), b
...
# 函數調用(實參是“位置參數”)
>>> func() # 個數必須相等
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 2 arguments (0 given)
>>> func(1)
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 2 arguments (1 given)
>>> func(1, 2, 3)
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 2 arguments (3 given)
>>> func(1, 2) # 順序一致時:a等于1,b等于2
1
2
>>> func(2, 1) # 順序相反時:a等于2,b等于1
2
1
# 函數調用(實參是“關鍵字參數”)
>>> func(a=1, b=2) # 順序一致時:a等于1,b等于2
1
2
>>> func(b=2, a=1) # 順序相反時:a等于1,b等于2
1
2
>>> func(c=1, d=2) # 名稱必須一致
Traceback (most recent call last):
File "", line 1, in
TypeError: func() got an unexpected keyword argument 'c'
2)默認參數
在函數定義中,默認參數 被指定了默認值,因此在調用函數時:
如果不指定對應的實參,則形參使用其默認值
如果指定了對應的實參,則形參使用實際指定的參數值
指定實參時,默認參數的綁定規則與“常規參數”相同
參考以下示例:
# 函數定義
>>> def func(a=1):
... print type(a), a
...
# 函數調用
>>> func() # 使用默認值
1
>>> func(2) # 實參是“位置參數”
2
>>> func(a=2) # 實參是“關鍵字參數”
2
對于默認參數,還有一點值得注意的是:默認參數只在函數定義(即執行def語句)時被求值一次,以后每次調用函數時都使用以前的值(參考 function definitions)。由此可知,當默認參數的默認值是一個可變對象的時候,如果函數內部對默認參數有修改,就會影響到下一次調用函數時的默認值(一般情況下,這可能不是你想要的行為)。簡單示例如下:
# 函數定義
>>> def func(a=[]):
... print a
... a.append(0)
...
# 函數調用
>>> func()
[]
>>> func()
[0]
>>> func()
[0, 0]
為了避免上述問題,可以采用以下方式:
# 函數定義
>>> def func(a=None):
... if a is None:
... a = []
... print a
... a.append(0)
...
# 函數調用
>>> func()
[]
>>> func()
[]
3)變長元組參數
在“常規參數”和“默認參數”綁定完成后(如果有的話),如果還有額外(0個或多個)的“位置參數”,則 變長元組參數 將會把這些多余的“位置參數”以 元組 的形式搜集到一起。示例如下:
# 函數定義
>>> def func(*args):
... print type(args), args
...
# 函數調用
>>> func() # 允許沒有“位置參數”
()
>>> func(1, 2) # 實參是“位置參數”
(1, 2)
>>> func(*(1, 2)) # 實參是“位置參數”
(1, 2)
>>> func(a=1, b=2) # 不接受實參是“關鍵字參數”的情況
Traceback (most recent call last):
File "", line 1, in
TypeError: func() got an unexpected keyword argument 'a'
4)變長字典參數
在“常規參數”和“默認參數”綁定完成后(如果有的話),如果還有額外(0個或多個)的“關鍵字參數”,則 變長字典參數 將會把這些多余的“關鍵字參數”以 字典 的形式搜集到一起。示例如下:
# 函數定義
>>> def func(**kwargs):
... print type(kwargs), kwargs
...
# 函數調用
>>> func() # 允許沒有“關鍵字參數”
{}
>>> func(a=1, b=2) # 實參是“關鍵字參數”
{'a': 1, 'b': 2}
>>> func(**{'a': 1, 'b': 2}) # 實參是“關鍵字參數”
{'a': 1, 'b': 2}
>>> func(1, 2) # 不接受實參是“位置參數”的情況
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 0 arguments (2 given)
5)混合使用
如果在函數定義中,要混合使用上述四種類型的形參,則這幾種形參類型的排列順序必須從左到右依次為:常規參數,默認參數,變長元組參數,變長字典參數。
以下為混合使用幾種形參的典型示例:
# 函數定義
>>> def func(a, b=0, *args, **kwargs):
... print type(a), a
... print type(b), b
... print type(args), args
... print type(kwargs), kwargs
...
# 函數調用
>>> func(1)
1
0
()
{}
>>> func(1, 2)
1
2
()
{}
>>> func(1, 2, 3)
1
2
(3,)
{}
>>> func(1, 2, 3, c=4)
1
2
(3,)
{'c': 4}
四、返回值
在Python中,一個函數總會返回一個值(除非發生異常),這個值可以是任何Python對象。根據具體函數的不同,返回值有以下幾種情況:
函數中的return語句實際返回的Python對象
無return
None
return
None
return a
對象a
return a, b, c
元組(a, b, c)
簡單示例如下:
>>> def f1(): pass
...
>>> def f2(): return
...
>>> def f3(): return 1
...
>>> def f4(): return 1, 2, 3
...
>>> f1(), f2(), f3(), f4()
(None, None, 1, (1, 2, 3))
五、名字空間與作用域
以下討論中,會根據下面的 示意圖 來進行具體示例分析:
1、基本概念
在Python程序中,一切對象都是借助 名字 來操作的(即名字引用對象)。名字空間(namespace)是名字到對象的映射。
在一個程序文本中,通常存在多個不同的 代碼塊(code block),例如模塊、函數體、類定義等,每個代碼塊都對應一個獨立的名字空間。名字空間中的名字只能在一個代碼范圍內可見,這個代碼范圍稱為 作用域(scope)。
對于上述概念的準確而詳細的描述,請參考 Naming and binding。
2、名字空間
一個代碼塊對應一個名字空間,具體到示意圖中的情況:
func局部名字空間:即func函數的代碼塊對應的名字空間,包含變量名e,函數參數名x、y、z
func_inner局部名字空間:即func_inner函數的代碼塊(除開func部分)對應的名字空間,包含變量名c,函數名func,函數參數名x、y
func_outer局部名字空間:即func_outer函數的代碼塊(除開func_inner部分)對應的名字空間,包含變量名b,函數名func_inner,函數參數x
全局名字空間:即模塊文件的代碼塊(除開func_outer部分)對應的名字空間,包含變量名a,函數名func_outer,變量名d(在func函數中以global方式定義),以及Python為模塊預置的一些名字(例如__name__、__builtins__等)
內建名字空間:包含內建模塊__builtin__中的所有名字,例如print(實際由全局名字空間中的__builtins__指定:在__main__模塊中,__builtins__就是內建的__builtin__模塊;在導入模塊中,__builtins__是字典__builtin__.__dict__的別名)
查看名字空間的一個簡單方法是:使用dir函數。例如,查看示意圖中各代碼塊對應名字空間的示例如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
a = 1
def func_outer(x):
b = 2
def func_inner(y):
c = 3
def func(z):
global d
d = 4
e = x + y + z
print "func's namespace:"
print dir()
func(0)
print "func_inner's namespace:"
print dir()
func_inner(0)
print "func_outer's namespace:"
print dir()
if __name__ == '__main__':
func_outer(0)
print 'global namespace:'
print dir()
print 'built-in namespace:'
print dir(__builtins__)
運行結果:
$ python shownamespace.py
func's namespace:
['e', 'x', 'y', 'z']
func_inner's namespace:
['c', 'func', 'x', 'y']
func_outer's namespace:
['b', 'func_inner', 'x']
global namespace:
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'd', 'func_outer']
built-in namespace:
['ArithmeticError', 'AssertionError', ..., 'xrange', 'zip']
3、作用域
Python中的作用域可以分為四種:局部作用域(Local scope)、外圍作用域(Enclosing scope)、全局作用域(Global scope)、內建作用域(Built-in scope)。
例如,對于示意圖中的函數func而言:
局部作用域 是指func函數對應的代碼范圍(包含在def語句或lambda表達式內部)
外圍作用域 是指func_outer函數對應的代碼范圍(包括內嵌的函數funct_inner,以及二次內嵌的函數func)
全局作用域 是指整個模塊文件對應的代碼范圍
內建作用域 是指任何的Python代碼范圍
上述幾種名字空間與這四種作用域的關系是:
func局部名字空間中的名字只在局部作用域可見
func_inner局部名字空間和func_outer局部名字空間中的名字只在外圍作用域可見
全局名字空間中的名字只在全局作用域可見
內建名字空間中的名字只在內建作用域可見
4、總原則
關于名字與作用域,主要記住以下兩點:
名字引用 遵循 LEGB 的查找規則:首先查找局部作用域(L),接著查找外圍作用域(E),然后查找全局作用域(G),最后查找內建作用域(B);否則查找失敗
名字賦值 默認新建一個局部作用域中的名字;如果與LEGB路徑上的其他作用域中的名字相同,則在LEGB查找時將屏蔽其他作用域中的相同名字;如果聲明為global,則將新建(或是覆蓋,如果已存在)一個全局作用域中的名字
六、高級
1、裝飾器
裝飾器(decorator)是一個函數,它對另一個函數進行包裝處理,進而擴展被包裝函數的功能。盡管名稱相同,但Python中的裝飾器并不是設計模式中的裝飾器模式的Python實現(可以參考 Decorators)。
@wrapper[(arg)]
def funcname([parameters]):
裝飾器可以不帶參數,例如以下兩種函數定義是等價的:
# 裝飾器版本
@f
def func(): pass
# 普通版本
def func(): pass
func = f(func)
裝飾器也可以帶參數,例如以下兩種函數定義是等價的:
# 裝飾器版本
@f(arg)
def func(): pass
# 普通版本
def func(): pass
func = f(arg)(func)
當然,還可以多個裝飾器嵌套使用,例如以下兩種函數定義是等價的:
# 裝飾器版本
@f1(arg)
@f2
def func(): pass
# 普通版本
def func(): pass
func = f1(arg)(f2(func))
以上都是函數定義中的裝飾器語法,下面給出一個裝飾器實現的簡單示例:
>>> def wrapper(func):
... def wrappedFunc():
... print 'before func'
... func()
... print 'after func'
... return wrappedFunc
...
>>> @wrapper
... def func():
... print 'in func'
...
>>> func()
before func
in func
after func
2、生成器
生成器(generator)是一個帶有yield語句的函數,與普通函數不同的是,它返回一個支持迭代器(iterator)協議的對象。
以下是一個生成器的簡單示例:
# 平方生成器
>>> def squares(N):
... for i in range(N):
... yield i ** 2
...
# 返回一個生成器對象,該對象支持迭代器協議
>>> x = squares(2)
>>> x
>>> x.next()
0
>>> x.next()
1
>>> x.next()
Traceback (most recent call last):
File "", line 1, in
StopIteration
# for循環自動調用next(),并處理StopIteration異常
>>> for i in squares(2):
... print i
...
0
1
如果要嚴格區分的話,在上述示例中,squares 是生成器函數,而 x 才是生成器(對象)。可以借助 inspect模塊 來體會二者的區別(參考 Python yield使用淺析):
>>> import inspect
>>> inspect.isgeneratorfunction(squares), inspect.isgeneratorfunction(x)
(True, False)
>>> inspect.isgenerator(squares), inspect.isgenerator(x)
(False, True)
關于生成器,需要注意以下幾點:
yield語句會產生一個值,作為next()調用的返回值
yield語句會中斷函數處理,并記住當前的執行狀態(中斷位置和變量值等),以便后續原狀態恢復執行
如果恢復執行后,已經沒有yield語句可執行,則拋出StopIteration異常(以示迭代結束)
生成器中一般沒有return語句,如果執行中遇到了return語句,則直接拋出StopIteration異常
總結
以上是生活随笔為你收集整理的python基础函数图_Python基础:函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 辽宁舰会改为弹射型航母吗?
- 下一篇: python英文高频句统计_使用Pyth