返回值是None,類型也是'NoneType'。
成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站制作、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的安義網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
Python是一種廣泛使用的解釋型、高級和通用的編程語言。Python由荷蘭數(shù)學(xué)和計(jì)算機(jī)科學(xué)研究學(xué)會(huì)的GuidovanRossum創(chuàng)造,第一版發(fā)布于1991年,它是ABC語言的后繼者,也可以視之為一種使用傳統(tǒng)中綴表達(dá)式的LISP方言。Python提供了高效的高級數(shù)據(jù)結(jié)構(gòu),還能簡單有效地面向?qū)ο缶幊獭?/p>
當(dāng)然可以了。
Python中有個(gè)元類的概念, metaclass就是用來生成類的類。
還有工廠函數(shù)的概念,就是說一個(gè)函數(shù)直接返回一個(gè)類。
class?MetaDog:
def?__init__(self,?color=''):
self.color?=?color
def?show_color(self):
print('my?dog?color?is?{}.'.format(self.color))
class?Animal:
def?dog_class(self):
return?MetaDog
animal?=?Animal()
Dog?=?animal.dog_class()
my_dog?=?Dog()
my_dog.color?=?'yellow'
#?my_dog?=?Dog('yellow')
my_dog.show_color()
#?my?dog?color?is?yellow.
python沒有靜態(tài)類型,因此不需要也不可以指定函數(shù)的返回類型。
依據(jù)業(yè)務(wù)邏輯的需要,一個(gè)函數(shù)可以返回任何值,也可以什么都不返回。
比如:
def f1():return 1
def f2():return 'ok'
def f3():return [1,2,'good']
“在Python中,函數(shù)本身也是對象”
這一本質(zhì)。那不妨慢慢來,從最基本的概念開始,討論一下這個(gè)問題:
1. Python中一切皆對象
這恐怕是學(xué)習(xí)Python最有用的一句話。想必你已經(jīng)知道Python中的list, tuple, dict等內(nèi)置數(shù)據(jù)結(jié)構(gòu),當(dāng)你執(zhí)行:
alist = [1, 2, 3]
時(shí),你就創(chuàng)建了一個(gè)列表對象,并且用alist這個(gè)變量引用它:
當(dāng)然你也可以自己定義一個(gè)類:
class House(object):
def __init__(self, area, city):
self.area = area
self.city = city
def sell(self, price):
[...] #other code
return price
然后創(chuàng)建一個(gè)類的對象:
house = House(200, 'Shanghai')
OK,你立馬就在上海有了一套200平米的房子,它有一些屬性(area, city),和一些方法(__init__, self):
2. 函數(shù)是第一類對象
和list, tuple, dict以及用House創(chuàng)建的對象一樣,當(dāng)你定義一個(gè)函數(shù)時(shí),函數(shù)也是對象:
def func(a, b):
return a+b
在全局域,函數(shù)對象被函數(shù)名引用著,它接收兩個(gè)參數(shù)a和b,計(jì)算這兩個(gè)參數(shù)的和作為返回值。
所謂第一類對象,意思是可以用標(biāo)識(shí)符給對象命名,并且對象可以被當(dāng)作數(shù)據(jù)處理,例如賦值、作為參數(shù)傳遞給函數(shù),或者作為返回值return 等
因此,你完全可以用其他變量名引用這個(gè)函數(shù)對象:
add = func
這樣,你就可以像調(diào)用func(1, 2)一樣,通過新的引用調(diào)用函數(shù)了:
print func(1, 2)
print add(1, 2) #the same as func(1, 2)
或者將函數(shù)對象作為參數(shù),傳遞給另一個(gè)函數(shù):
def caller_func(f):
return f(1, 2)
if __name__ == "__main__":
print caller_func(func)
可以看到,
函數(shù)對象func作為參數(shù)傳遞給caller_func函數(shù),傳參過程類似于一個(gè)賦值操作f=func;
于是func函數(shù)對象,被caller_func函數(shù)作用域中的局部變量f引用,f實(shí)際指向了函數(shù)func;cc
當(dāng)執(zhí)行return f(1, 2)的時(shí)候,相當(dāng)于執(zhí)行了return func(1, 2);
因此輸出結(jié)果為3。
3. 函數(shù)對象 vs 函數(shù)調(diào)用
無論是把函數(shù)賦值給新的標(biāo)識(shí)符,還是作為參數(shù)傳遞給新的函數(shù),針對的都是函數(shù)對象本身,而不是函數(shù)的調(diào)用。
用一個(gè)更加簡單,但從外觀上看,更容易產(chǎn)生混淆的例子來說明這個(gè)問題。例如定義了下面這個(gè)函數(shù):
def func():
return "hello,world"
然后分別執(zhí)行兩次賦值:
ref1 = func #將函數(shù)對象賦值給ref1
ref2 = func() #調(diào)用函數(shù),將函數(shù)的返回值("hello,world"字符串)賦值給ref2
很多初學(xué)者會(huì)混淆這兩種賦值,通過Python內(nèi)建的type函數(shù),可以查看一下這兩次賦值的結(jié)果:
In [4]: type(ref1)
Out[4]: function
In [5]: type(ref2)
Out[5]: str
可以看到,ref1引用了函數(shù)對象本身,而ref2則引用了函數(shù)的返回值。通過內(nèi)建的callable函數(shù),可以進(jìn)一步驗(yàn)證ref1是可調(diào)用的,而ref2是不可調(diào)用的:
In [9]: callable(ref1)
Out[9]: True
In [10]: callable(ref2)
Out[10]: False
傳參的效果與之類似。
4. 閉包LEGB法則
所謂閉包,就是將組成函數(shù)的語句和這些語句的執(zhí)行環(huán)境打包在一起時(shí),得到的對象
聽上去的確有些復(fù)雜,還是用一個(gè)栗子來幫助理解一下。假設(shè)我們在foo.py模塊中做了如下定義:
#foo.py
filename = "foo.py"
def call_func(f):
return f() #如前面介紹的,f引用一個(gè)函數(shù)對象,然后調(diào)用它
在另一個(gè)func.py模塊中,寫下了這樣的代碼:
#func.py
import foo #導(dǎo)入foo.py
filename = "func.py"
def show_filename():
return "filename: %s" % filename
if __name__ == "__main__":
print foo.call_func(show_filename) #注意:實(shí)際發(fā)生調(diào)用的位置,是在foo.call_func函數(shù)中
當(dāng)我們用python func.py命令執(zhí)行func.py時(shí)輸出結(jié)果為:
chiyu@chiyu-PC:~$ python func.py
filename:func.py
很顯然show_filename()函數(shù)使用的filename變量的值,是在與它相同環(huán)境(func.py模塊)中定義的那個(gè)。盡管foo.py模塊中也定義了同名的filename變量,而且實(shí)際調(diào)用show_filename的位置也是在foo.py的call_func內(nèi)部。
而對于嵌套函數(shù),這一機(jī)制則會(huì)表現(xiàn)的更加明顯:閉包將會(huì)捕捉內(nèi)層函數(shù)執(zhí)行所需的整個(gè)環(huán)境:
#enclosed.py
import foo
def wrapper():
filename = "enclosed.py"
def show_filename():
return "filename: %s" % filename
print foo.call_func(show_filename) #輸出:filename: enclosed.py
實(shí)際上,每一個(gè)函數(shù)對象,都有一個(gè)指向了該函數(shù)定義時(shí)所在全局名稱空間的__globals__屬性:
#show_filename inside wrapper
#show_filename.__globals__
{
'__builtins__': module '__builtin__' (built-in), #內(nèi)建作用域環(huán)境
'__file__': 'enclosed.py',
'wrapper': function wrapper at 0x7f84768b6578, #直接外圍環(huán)境
'__package__': None,
'__name__': '__main__',
'foo': module 'foo' from '/home/chiyu/foo.pyc', #全局環(huán)境
'__doc__': None
}
當(dāng)代碼執(zhí)行到show_filename中的return "filename: %s" % filename語句時(shí),解析器按照下面的順序查找filename變量:
Local - 本地函數(shù)(show_filename)內(nèi)部,通過任何方式賦值的,而且沒有被global關(guān)鍵字聲明為全局變量的filename變量;
Enclosing - 直接外圍空間(上層函數(shù)wrapper)的本地作用域,查找filename變量(如果有多層嵌套,則由內(nèi)而外逐層查找,直至最外層的函數(shù));
Global - 全局空間(模塊enclosed.py),在模塊頂層賦值的filename變量;
Builtin - 內(nèi)置模塊(__builtin__)中預(yù)定義的變量名中查找filename變量;
在任何一層先找到了符合要求的filename變量,則不再向更外層查找。如果直到Builtin層仍然沒有找到符合要求的變量,則拋出NameError異常。這就是變量名解析的:LEGB法則。
總結(jié):
閉包最重要的使用價(jià)值在于:封存函數(shù)執(zhí)行的上下文環(huán)境;
閉包在其捕捉的執(zhí)行環(huán)境(def語句塊所在上下文)中,也遵循LEGB規(guī)則逐層查找,直至找到符合要求的變量,或者拋出異常。
5. 裝飾器語法糖(syntax sugar)
那么閉包和裝飾器又有什么關(guān)系呢?
上文提到閉包的重要特性:封存上下文,這一特性可以巧妙的被用于現(xiàn)有函數(shù)的包裝,從而為現(xiàn)有函數(shù)更加功能。而這就是裝飾器。
還是舉個(gè)例子,代碼如下:
#alist = [1, 2, 3, ..., 100] -- 1+2+3+...+100 = 5050
def lazy_sum():
return reduce(lambda x, y: x+y, alist)
我們定義了一個(gè)函數(shù)lazy_sum,作用是對alist中的所有元素求和后返回。alist假設(shè)為1到100的整數(shù)列表:
alist = range(1, 101)
但是出于某種原因,我并不想馬上返回計(jì)算結(jié)果,而是在之后的某個(gè)地方,通過顯示的調(diào)用輸出結(jié)果。于是我用一個(gè)wrapper函數(shù)對其進(jìn)行包裝:
def wrapper():
alist = range(1, 101)
def lazy_sum():
return reduce(lambda x, y: x+y, alist)
return lazy_sum
lazy_sum = wrapper() #wrapper() 返回的是lazy_sum函數(shù)對象
if __name__ == "__main__":
lazy_sum() #5050
這是一個(gè)典型的Lazy Evaluation的例子。我們知道,一般情況下,局部變量在函數(shù)返回時(shí),就會(huì)被垃圾回收器回收,而不能再被使用。但是這里的alist卻沒有,它隨著lazy_sum函數(shù)對象的返回被一并返回了(這個(gè)說法不準(zhǔn)確,實(shí)際是包含在了lazy_sum的執(zhí)行環(huán)境中,通過__globals__),從而延長了生命周期。
當(dāng)在if語句塊中調(diào)用lazy_sum()的時(shí)候,解析器會(huì)從上下文中(這里是Enclosing層的wrapper函數(shù)的局部作用域中)找到alist列表,計(jì)算結(jié)果,返回5050。
當(dāng)你需要?jiǎng)討B(tài)的給已定義的函數(shù)增加功能時(shí),比如:參數(shù)檢查,類似的原理就變得很有用:
def add(a, b):
return a+b
這是很簡單的一個(gè)函數(shù):計(jì)算a+b的和返回,但我們知道Python是 動(dòng)態(tài)類型+強(qiáng)類型 的語言,你并不能保證用戶傳入的參數(shù)a和b一定是兩個(gè)整型,他有可能傳入了一個(gè)整型和一個(gè)字符串類型的值:
In [2]: add(1, 2)
Out[2]: 3
In [3]: add(1.2, 3.45)
Out[3]: 4.65
In [4]: add(5, 'hello')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/chiyu/ipython-input-4-f2f9e8aa5eae in module()
---- 1 add(5, 'hello')
/home/chiyu/ipython-input-1-02b3d3d6caec in add(a, b)
1 def add(a, b):
---- 2 return a+b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
于是,解析器無情的拋出了一個(gè)TypeError異常。
動(dòng)態(tài)類型:在運(yùn)行期間確定變量的類型,python確定一個(gè)變量的類型是在你第一次給他賦值的時(shí)候;
強(qiáng)類型:有強(qiáng)制的類型定義,你有一個(gè)整數(shù),除非顯示的類型轉(zhuǎn)換,否則絕不能將它當(dāng)作一個(gè)字符串(例如直接嘗試將一個(gè)整型和一個(gè)字符串做+運(yùn)算);
因此,為了更加優(yōu)雅的使用add函數(shù),我們需要在執(zhí)行+運(yùn)算前,對a和b進(jìn)行參數(shù)檢查。這時(shí)候裝飾器就顯得非常有用:
import logging
logging.basicConfig(level = logging.INFO)
def add(a, b):
return a + b
def checkParams(fn):
def wrapper(a, b):
if isinstance(a, (int, float)) and isinstance(b, (int, float)): #檢查參數(shù)a和b是否都為整型或浮點(diǎn)型
return fn(a, b) #是則調(diào)用fn(a, b)返回計(jì)算結(jié)果
#否則通過logging記錄錯(cuò)誤信息,并友好退出
logging.warning("variable 'a' and 'b' cannot be added")
return
return wrapper #fn引用add,被封存在閉包的執(zhí)行環(huán)境中返回
if __name__ == "__main__":
#將add函數(shù)對象傳入,fn指向add
#等號左側(cè)的add,指向checkParams的返回值wrapper
add = checkParams(add)
add(3, 'hello') #經(jīng)過類型檢查,不會(huì)計(jì)算結(jié)果,而是記錄日志并退出
注意checkParams函數(shù):
首先看參數(shù)fn,當(dāng)我們調(diào)用checkParams(add)的時(shí)候,它將成為函數(shù)對象add的一個(gè)本地(Local)引用;
在checkParams內(nèi)部,我們定義了一個(gè)wrapper函數(shù),添加了參數(shù)類型檢查的功能,然后調(diào)用了fn(a, b),根據(jù)LEGB法則,解釋器將搜索幾個(gè)作用域,并最終在(Enclosing層)checkParams函數(shù)的本地作用域中找到fn;
注意最后的return wrapper,這將創(chuàng)建一個(gè)閉包,fn變量(add函數(shù)對象的一個(gè)引用)將會(huì)封存在閉包的執(zhí)行環(huán)境中,不會(huì)隨著checkParams的返回而被回收;
當(dāng)調(diào)用add = checkParams(add)時(shí),add指向了新的wrapper對象,它添加了參數(shù)檢查和記錄日志的功能,同時(shí)又能夠通過封存的fn,繼續(xù)調(diào)用原始的add進(jìn)行+運(yùn)算。
因此調(diào)用add(3, 'hello')將不會(huì)返回計(jì)算結(jié)果,而是打印出日志:
chiyu@chiyu-PC:~$ python func.py
WARNING:root:variable 'a' and 'b' cannot be added
有人覺得add = checkParams(add)這樣的寫法未免太過麻煩,于是python提供了一種更優(yōu)雅的寫法,被稱為語法糖:
@checkParams
def add(a, b):
return a + b
這只是一種寫法上的優(yōu)化,解釋器仍然會(huì)將它轉(zhuǎn)化為add = checkParams(add)來執(zhí)行。
6. 回歸問題
def addspam(fn):
def new(*args):
print "spam,spam,spam"
return fn(*args)
return new
@addspam
def useful(a,b):
print a**2+b**2
首先看第二段代碼:
@addspam裝飾器,相當(dāng)于執(zhí)行了useful = addspam(useful)。在這里題主有一個(gè)理解誤區(qū):傳遞給addspam的參數(shù),是useful這個(gè)函數(shù)對象本身,而不是它的一個(gè)調(diào)用結(jié)果;
再回到addspam函數(shù)體:
return new 返回一個(gè)閉包,fn被封存在閉包的執(zhí)行環(huán)境中,不會(huì)隨著addspam函數(shù)的返回被回收;
而fn此時(shí)是useful的一個(gè)引用,當(dāng)執(zhí)行return fn(*args)時(shí),實(shí)際相當(dāng)于執(zhí)行了return useful(*args);
最后附上一張代碼執(zhí)行過程中的引用關(guān)系圖,希望能幫助你理解:
Python2.X range()函數(shù)可創(chuàng)建一個(gè)整數(shù)列表,一般用在for循環(huán)中。
Python3 range()函數(shù)返回的是一個(gè)可迭代對象,類型是對象,而不是列表類型,所以打印的時(shí)候不會(huì)打印列表。
Python3 list()函數(shù)是對象迭代器,可以把range()返回的可迭代對象轉(zhuǎn)為一個(gè)列表,返回的變量類型為列表。
Python2 range()函數(shù)返回的是列表。
Python range()函數(shù)語法
range(start, stop[,step])
參數(shù)說明:
start:計(jì)數(shù)從start開始,默認(rèn)是從0開始;比如:range(5)等價(jià)于range(0,5)。
stop:計(jì)數(shù)到stop結(jié)束,但不包括stop;比如:range(0,5)是[0, 1, 2, 3, 4]沒有5。
step:步長,默認(rèn)為1;比如:range(0,5)等價(jià)于range(0,5,1)。
函數(shù)可以說是一個(gè)黑箱,輸入一些值,然后輸出一些值,因此return就是讓函數(shù)輸出值的操作。
然而,類,簡單來說就是一系列函數(shù)的集合,它最主要的用途是設(shè)定對象和方法。
在Python中,我簡單舉個(gè)例子,我要算a+b=c,我輸入a和b,輸出c。
那么,函數(shù)就是這樣的:
def plus(a, b):
c = a + b
return c
這里你就可以看到,輸入兩個(gè)值,經(jīng)過函數(shù)內(nèi)部計(jì)算,就輸出的一個(gè)值。在主程序中你調(diào)用這個(gè)函數(shù),比如:c = plus(1,2),那么print c就得到3。
但是類是不同的,同樣是計(jì)算a+b=c,我要先設(shè)定一種方法,比如叫做Plus,如下:
Class Plus:
def __init__(self, a, b):
self.a = a
self.b = b
def return_result(self):
self.c = self.a + self.b
return self.c
那么在主程序中你就要調(diào)用這個(gè)類,如下:
equation = Plus(1, 2)
result = equation.return_result()
print result
這樣你就會(huì)得到結(jié)果3。
希望可以幫到你,或者你把你的程序發(fā)過來,我看看~
分享標(biāo)題:python中函數(shù)返回類 python中的返回函數(shù)
當(dāng)前URL:http://muchs.cn/article18/hjsgdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、虛擬主機(jī)、品牌網(wǎng)站設(shè)計(jì)、標(biāo)簽優(yōu)化、面包屑導(dǎo)航、外貿(mào)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)