“在Python中,函數(shù)本身也是對象”
洪山網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),洪山網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為洪山上千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的洪山做網(wǎng)站的公司定做!
這一本質(zhì)。那不妨慢慢來,從最基本的概念開始,討論一下這個問題:
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]
時,你就創(chuàng)建了一個列表對象,并且用alist這個變量引用它:
當(dāng)然你也可以自己定義一個類:
class House(object):
def __init__(self, area, city):
self.area = area
self.city = city
def sell(self, price):
[...] #other code
return price
然后創(chuàng)建一個類的對象:
house = House(200, 'Shanghai')
OK,你立馬就在上海有了一套200平米的房子,它有一些屬性(area, city),和一些方法(__init__, self):
2. 函數(shù)是第一類對象
和list, tuple, dict以及用House創(chuàng)建的對象一樣,當(dāng)你定義一個函數(shù)時,函數(shù)也是對象:
def func(a, b):
return a+b
在全局域,函數(shù)對象被函數(shù)名引用著,它接收兩個參數(shù)a和b,計(jì)算這兩個參數(shù)的和作為返回值。
所謂第一類對象,意思是可以用標(biāo)識符給對象命名,并且對象可以被當(dāng)作數(shù)據(jù)處理,例如賦值、作為參數(shù)傳遞給函數(shù),或者作為返回值return 等
因此,你完全可以用其他變量名引用這個函數(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ù),傳遞給另一個函數(shù):
def caller_func(f):
return f(1, 2)
if __name__ == "__main__":
print caller_func(func)
可以看到,
函數(shù)對象func作為參數(shù)傳遞給caller_func函數(shù),傳參過程類似于一個賦值操作f=func;
于是func函數(shù)對象,被caller_func函數(shù)作用域中的局部變量f引用,f實(shí)際指向了函數(shù)func;cc
當(dāng)執(zhí)行return f(1, 2)的時候,相當(dāng)于執(zhí)行了return func(1, 2);
因此輸出結(jié)果為3。
3. 函數(shù)對象 vs 函數(shù)調(diào)用
無論是把函數(shù)賦值給新的標(biāo)識符,還是作為參數(shù)傳遞給新的函數(shù),針對的都是函數(shù)對象本身,而不是函數(shù)的調(diào)用。
用一個更加簡單,但從外觀上看,更容易產(chǎn)生混淆的例子來說明這個問題。例如定義了下面這個函數(shù):
def func():
return "hello,world"
然后分別執(zhí)行兩次賦值:
ref1 = func #將函數(shù)對象賦值給ref1
ref2 = func() #調(diào)用函數(shù),將函數(shù)的返回值("hello,world"字符串)賦值給ref2
很多初學(xué)者會混淆這兩種賦值,通過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)境打包在一起時,得到的對象
聽上去的確有些復(fù)雜,還是用一個栗子來幫助理解一下。假設(shè)我們在foo.py模塊中做了如下定義:
#foo.py
filename = "foo.py"
def call_func(f):
return f() #如前面介紹的,f引用一個函數(shù)對象,然后調(diào)用它
在另一個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時輸出結(jié)果為:
chiyu@chiyu-PC:~$ python func.py
filename:func.py
很顯然show_filename()函數(shù)使用的filename變量的值,是在與它相同環(huán)境(func.py模塊)中定義的那個。盡管foo.py模塊中也定義了同名的filename變量,而且實(shí)際調(diào)用show_filename的位置也是在foo.py的call_func內(nèi)部。
而對于嵌套函數(shù),這一機(jī)制則會表現(xiàn)的更加明顯:閉包將會捕捉內(nèi)層函數(shù)執(zhí)行所需的整個環(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í)際上,每一個函數(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語句時,解析器按照下面的順序查找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é):
閉包最重要的使用價值在于:封存函數(shù)執(zhí)行的上下文環(huán)境;
閉包在其捕捉的執(zhí)行環(huán)境(def語句塊所在上下文)中,也遵循LEGB規(guī)則逐層查找,直至找到符合要求的變量,或者拋出異常。
5. 裝飾器語法糖(syntax sugar)
那么閉包和裝飾器又有什么關(guān)系呢?
上文提到閉包的重要特性:封存上下文,這一特性可以巧妙的被用于現(xiàn)有函數(shù)的包裝,從而為現(xiàn)有函數(shù)更加功能。而這就是裝飾器。
還是舉個例子,代碼如下:
#alist = [1, 2, 3, ..., 100] -- 1+2+3+...+100 = 5050
def lazy_sum():
return reduce(lambda x, y: x+y, alist)
我們定義了一個函數(shù)lazy_sum,作用是對alist中的所有元素求和后返回。alist假設(shè)為1到100的整數(shù)列表:
alist = range(1, 101)
但是出于某種原因,我并不想馬上返回計(jì)算結(jié)果,而是在之后的某個地方,通過顯示的調(diào)用輸出結(jié)果。于是我用一個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
這是一個典型的Lazy Evaluation的例子。我們知道,一般情況下,局部變量在函數(shù)返回時,就會被垃圾回收器回收,而不能再被使用。但是這里的alist卻沒有,它隨著lazy_sum函數(shù)對象的返回被一并返回了(這個說法不準(zhǔn)確,實(shí)際是包含在了lazy_sum的執(zhí)行環(huán)境中,通過__globals__),從而延長了生命周期。
當(dāng)在if語句塊中調(diào)用lazy_sum()的時候,解析器會從上下文中(這里是Enclosing層的wrapper函數(shù)的局部作用域中)找到alist列表,計(jì)算結(jié)果,返回5050。
當(dāng)你需要動態(tài)的給已定義的函數(shù)增加功能時,比如:參數(shù)檢查,類似的原理就變得很有用:
def add(a, b):
return a+b
這是很簡單的一個函數(shù):計(jì)算a+b的和返回,但我們知道Python是 動態(tài)類型+強(qiáng)類型 的語言,你并不能保證用戶傳入的參數(shù)a和b一定是兩個整型,他有可能傳入了一個整型和一個字符串類型的值:
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'
于是,解析器無情的拋出了一個TypeError異常。
動態(tài)類型:在運(yùn)行期間確定變量的類型,python確定一個變量的類型是在你第一次給他賦值的時候;
強(qiáng)類型:有強(qiáng)制的類型定義,你有一個整數(shù),除非顯示的類型轉(zhuǎn)換,否則絕不能將它當(dāng)作一個字符串(例如直接嘗試將一個整型和一個字符串做+運(yùn)算);
因此,為了更加優(yōu)雅的使用add函數(shù),我們需要在執(zhí)行+運(yùn)算前,對a和b進(jìn)行參數(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記錄錯誤信息,并友好退出
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)過類型檢查,不會計(jì)算結(jié)果,而是記錄日志并退出
注意checkParams函數(shù):
首先看參數(shù)fn,當(dāng)我們調(diào)用checkParams(add)的時候,它將成為函數(shù)對象add的一個本地(Local)引用;
在checkParams內(nèi)部,我們定義了一個wrapper函數(shù),添加了參數(shù)類型檢查的功能,然后調(diào)用了fn(a, b),根據(jù)LEGB法則,解釋器將搜索幾個作用域,并最終在(Enclosing層)checkParams函數(shù)的本地作用域中找到fn;
注意最后的return wrapper,這將創(chuàng)建一個閉包,fn變量(add函數(shù)對象的一個引用)將會封存在閉包的執(zhí)行環(huán)境中,不會隨著checkParams的返回而被回收;
當(dāng)調(diào)用add = checkParams(add)時,add指向了新的wrapper對象,它添加了參數(shù)檢查和記錄日志的功能,同時又能夠通過封存的fn,繼續(xù)調(diào)用原始的add進(jìn)行+運(yùn)算。
因此調(diào)用add(3, 'hello')將不會返回計(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)化,解釋器仍然會將它轉(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)。在這里題主有一個理解誤區(qū):傳遞給addspam的參數(shù),是useful這個函數(shù)對象本身,而不是它的一個調(diào)用結(jié)果;
再回到addspam函數(shù)體:
return new 返回一個閉包,fn被封存在閉包的執(zhí)行環(huán)境中,不會隨著addspam函數(shù)的返回被回收;
而fn此時是useful的一個引用,當(dāng)執(zhí)行return fn(*args)時,實(shí)際相當(dāng)于執(zhí)行了return useful(*args);
最后附上一張代碼執(zhí)行過程中的引用關(guān)系圖,希望能幫助你理解:
Python 是一種解釋型語言。這就是說,與C 語言和C 的衍生語言不同,Python 代碼在運(yùn)行之前不需要編譯。其他解釋型語言還包括PHP 和Ruby。
- Python 是動態(tài)類型語言,指的是你在聲明變量時,不需要說明變量的類型。你可以直接編寫類似x=111 和x="I’m a string"這樣的代碼,程序不會報(bào)錯。
- Python 非常適合面向?qū)ο蟮木幊蹋∣OP),因?yàn)樗С滞ㄟ^組合(composition)與繼承(inheritance)的方式定義類(class)。
- Python 中沒有訪問說明符(access specifier,類似C++中的public 和private),這么設(shè)計(jì)的依據(jù)是“大家都是成年人了”。
- 在Python 語言中,函數(shù)是第一類對象(first-class objects)。這指的是它們可以被指定給變量,函數(shù)既能返回函數(shù)類型,也可以接受函數(shù)作為輸入。類(class)也是第一類對象。
- Python 代碼編寫快,但是運(yùn)行速度比編譯語言通常要慢。好在Python 允許加入基于C語言編寫的擴(kuò)展,因此我們能夠優(yōu)化代碼,消除瓶頸,這點(diǎn)通常是可以實(shí)現(xiàn)的。numpy 就是一個很好地例子,它的運(yùn)行速度真的非常快,因?yàn)楹芏嗨阈g(shù)運(yùn)算其實(shí)并不是通過Python 實(shí)現(xiàn)的。
- Python 用途非常廣泛——網(wǎng)絡(luò)應(yīng)用,自動化,科學(xué)建模,大數(shù)據(jù)應(yīng)用,等等。它也常被用作“膠水語言”,幫助其他語言和組件改善運(yùn)行狀況。
- Python 讓困難的事情變得容易,因此程序員可以專注于算法和數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì),而不用處理底層的細(xì)節(jié)。
黑馬程序員含有全套的介紹,并且有和其他語言的對比。他們很多公開課也說過。我就是看黑馬的課學(xué)的Python,祝好
Python 函數(shù)
函數(shù)是組織好的,可重復(fù)使用的,用來實(shí)現(xiàn)單一,或相關(guān)聯(lián)功能的代碼段。
函數(shù)能提高應(yīng)用的模塊性,和代碼的重復(fù)利用率。你已經(jīng)知道Python提供了許多內(nèi)建函數(shù),比如print()。但你也可以自己創(chuàng)建函數(shù),這被叫做用戶自定義函數(shù)。
定義一個函數(shù)
你可以定義一個由自己想要功能的函數(shù),以下是簡單的規(guī)則:
函數(shù)代碼塊以?def?關(guān)鍵詞開頭,后接函數(shù)標(biāo)識符名稱和圓括號()。
任何傳入?yún)?shù)和自變量必須放在圓括號中間。圓括號之間可以用于定義參數(shù)。
函數(shù)的第一行語句可以選擇性地使用文檔字符串—用于存放函數(shù)說明。
函數(shù)內(nèi)容以冒號起始,并且縮進(jìn)。
return [表達(dá)式]?結(jié)束函數(shù),選擇性地返回一個值給調(diào)用方。不帶表達(dá)式的return相當(dāng)于返回 None。
語法
def functionname( parameters ): ? "函數(shù)_文檔字符串"
function_suite
return [expression]
默認(rèn)情況下,參數(shù)值和參數(shù)名稱是按函數(shù)聲明中定義的順序匹配起來的。
實(shí)例
以下為一個簡單的Python函數(shù),它將一個字符串作為傳入?yún)?shù),再打印到標(biāo)準(zhǔn)顯示設(shè)備上。
實(shí)例(Python 2.0+)
def printme( str ): ? "打印傳入的字符串到標(biāo)準(zhǔn)顯示設(shè)備上"
print str
return
函數(shù)調(diào)用
定義一個函數(shù)只給了函數(shù)一個名稱,指定了函數(shù)里包含的參數(shù),和代碼塊結(jié)構(gòu)。
這個函數(shù)的基本結(jié)構(gòu)完成以后,你可以通過另一個函數(shù)調(diào)用執(zhí)行,也可以直接從Python提示符執(zhí)行。
如下實(shí)例調(diào)用了printme()函數(shù):
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
# 定義函數(shù)def printme( str ): ? "打印任何傳入的字符串"
print str
return
# 調(diào)用函數(shù)printme("我要調(diào)用用戶自定義函數(shù)!")printme("再次調(diào)用同一函數(shù)")
以上實(shí)例輸出結(jié)果:
我要調(diào)用用戶自定義函數(shù)!再次調(diào)用同一函數(shù)
參數(shù)傳遞
在 python 中,類型屬于對象,變量是沒有類型的:
a=[1,2,3]
a="Runoob"
以上代碼中,[1,2,3]?是 List 類型,"Runoob"?是 String 類型,而變量 a 是沒有類型,她僅僅是一個對象的引用(一個指針),可以是 List 類型對象,也可以指向 String 類型對象。
可更改(mutable)與不可更改(immutable)對象
在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而 list,dict 等則是可以修改的對象。
不可變類型:變量賦值?a=5?后再賦值?a=10,這里實(shí)際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當(dāng)于新生成了a。
可變類型:變量賦值?la=[1,2,3,4]?后再賦值?la[2]=5?則是將 list la 的第三個元素值更改,本身la沒有動,只是其內(nèi)部的一部分值被修改了。
python 函數(shù)的參數(shù)傳遞:
不可變類型:類似 c++ 的值傳遞,如 整數(shù)、字符串、元組。如fun(a),傳遞的只是a的值,沒有影響a對象本身。比如在 fun(a)內(nèi)部修改 a 的值,只是修改另一個復(fù)制的對象,不會影響 a 本身。
可變類型:類似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改后fun外部的la也會受影響
python 中一切都是對象,嚴(yán)格意義我們不能說值傳遞還是引用傳遞,我們應(yīng)該說傳不可變對象和傳可變對象。
python 傳不可變對象實(shí)例
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
def ChangeInt( a ): ? ?a = 10
b = 2ChangeInt(b)print b # 結(jié)果是 2
實(shí)例中有 int 對象 2,指向它的變量是 b,在傳遞給 ChangeInt 函數(shù)時,按傳值的方式復(fù)制了變量 b,a 和 b 都指向了同一個 Int 對象,在 a=10 時,則新生成一個 int 值對象 10,并讓 a 指向它。
傳可變對象實(shí)例
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
# 可寫函數(shù)說明def changeme( mylist ): ? "修改傳入的列表"
mylist.append([1,2,3,4])
print "函數(shù)內(nèi)取值: ", mylist
return
# 調(diào)用changeme函數(shù)mylist = [10,20,30]changeme( mylist )print "函數(shù)外取值: ", mylist
實(shí)例中傳入函數(shù)的和在末尾添加新內(nèi)容的對象用的是同一個引用,故輸出結(jié)果如下:
函數(shù)內(nèi)取值: ?[10, 20, 30, [1, 2, 3, 4]]函數(shù)外取值: ?[10, 20, 30, [1, 2, 3, 4]]
參數(shù)
以下是調(diào)用函數(shù)時可使用的正式參數(shù)類型:
必備參數(shù)
關(guān)鍵字參數(shù)
默認(rèn)參數(shù)
不定長參數(shù)
必備參數(shù)
必備參數(shù)須以正確的順序傳入函數(shù)。調(diào)用時的數(shù)量必須和聲明時的一樣。
調(diào)用printme()函數(shù),你必須傳入一個參數(shù),不然會出現(xiàn)語法錯誤:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
#可寫函數(shù)說明def printme( str ): ? "打印任何傳入的字符串"
print str
return
#調(diào)用printme函數(shù)printme()
以上實(shí)例輸出結(jié)果:
Traceback (most recent call last):
File "test.py", line 11, in module
printme()TypeError: printme() takes exactly 1 argument (0 given)
關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)和函數(shù)調(diào)用關(guān)系緊密,函數(shù)調(diào)用使用關(guān)鍵字參數(shù)來確定傳入的參數(shù)值。
使用關(guān)鍵字參數(shù)允許函數(shù)調(diào)用時參數(shù)的順序與聲明時不一致,因?yàn)?Python 解釋器能夠用參數(shù)名匹配參數(shù)值。
以下實(shí)例在函數(shù) printme() 調(diào)用時使用參數(shù)名:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
#可寫函數(shù)說明def printme( str ): ? "打印任何傳入的字符串"
print str
return
#調(diào)用printme函數(shù)printme( str = "My string")
以上實(shí)例輸出結(jié)果:
My string
下例能將關(guān)鍵字參數(shù)順序不重要展示得更清楚:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
#可寫函數(shù)說明def printinfo( name, age ): ? "打印任何傳入的字符串"
print "Name: ", name
print "Age ", age
return
#調(diào)用printinfo函數(shù)printinfo( age=50, name="miki" )
以上實(shí)例輸出結(jié)果:
Name: ?mikiAge ?50
默認(rèn)參數(shù)
調(diào)用函數(shù)時,默認(rèn)參數(shù)的值如果沒有傳入,則被認(rèn)為是默認(rèn)值。下例會打印默認(rèn)的age,如果age沒有被傳入:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
#可寫函數(shù)說明def printinfo( name, age = 35 ): ? "打印任何傳入的字符串"
print "Name: ", name
print "Age ", age
return
#調(diào)用printinfo函數(shù)printinfo( age=50, name="miki" )printinfo( name="miki" )
以上實(shí)例輸出結(jié)果:
Name: ?mikiAge ?50Name: ?mikiAge ?35
不定長參數(shù)
你可能需要一個函數(shù)能處理比當(dāng)初聲明時更多的參數(shù)。這些參數(shù)叫做不定長參數(shù),和上述2種參數(shù)不同,聲明時不會命名?;菊Z法如下:
def functionname([formal_args,] *var_args_tuple ): ? "函數(shù)_文檔字符串"
function_suite
return [expression]
加了星號(*)的變量名會存放所有未命名的變量參數(shù)。不定長參數(shù)實(shí)例如下:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
# 可寫函數(shù)說明def printinfo( arg1, *vartuple ): ? "打印任何傳入的參數(shù)"
print "輸出: "
print arg1
for var in vartuple: ? ? ?print var
return
# 調(diào)用printinfo 函數(shù)printinfo( 10 )printinfo( 70, 60, 50 )
以上實(shí)例輸出結(jié)果:
輸出:10輸出:706050
匿名函數(shù)
python 使用 lambda 來創(chuàng)建匿名函數(shù)。
lambda只是一個表達(dá)式,函數(shù)體比def簡單很多。
lambda的主體是一個表達(dá)式,而不是一個代碼塊。僅僅能在lambda表達(dá)式中封裝有限的邏輯進(jìn)去。
lambda函數(shù)擁有自己的命名空間,且不能訪問自有參數(shù)列表之外或全局命名空間里的參數(shù)。
雖然lambda函數(shù)看起來只能寫一行,卻不等同于C或C++的內(nèi)聯(lián)函數(shù),后者的目的是調(diào)用小函數(shù)時不占用棧內(nèi)存從而增加運(yùn)行效率。
語法
lambda函數(shù)的語法只包含一個語句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
如下實(shí)例:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
# 可寫函數(shù)說明sum = lambda arg1, arg2: arg1 + arg2
# 調(diào)用sum函數(shù)print "相加后的值為 : ", sum( 10, 20 )print "相加后的值為 : ", sum( 20, 20 )
以上實(shí)例輸出結(jié)果:
相加后的值為 : ?30相加后的值為 : ?40
return 語句
return語句[表達(dá)式]退出函數(shù),選擇性地向調(diào)用方返回一個表達(dá)式。不帶參數(shù)值的return語句返回None。之前的例子都沒有示范如何返回?cái)?shù)值,下例便告訴你怎么做:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
# 可寫函數(shù)說明def sum( arg1, arg2 ): ? # 返回2個參數(shù)的和."
total = arg1 + arg2
print "函數(shù)內(nèi) : ", total
return total
# 調(diào)用sum函數(shù)total = sum( 10, 20 )
以上實(shí)例輸出結(jié)果:
函數(shù)內(nèi) : ?30
變量作用域
一個程序的所有的變量并不是在哪個位置都可以訪問的。訪問權(quán)限決定于這個變量是在哪里賦值的。
變量的作用域決定了在哪一部分程序你可以訪問哪個特定的變量名稱。兩種最基本的變量作用域如下:
全局變量
局部變量
全局變量和局部變量
定義在函數(shù)內(nèi)部的變量擁有一個局部作用域,定義在函數(shù)外的擁有全局作用域。
局部變量只能在其被聲明的函數(shù)內(nèi)部訪問,而全局變量可以在整個程序范圍內(nèi)訪問。調(diào)用函數(shù)時,所有在函數(shù)內(nèi)聲明的變量名稱都將被加入到作用域中。如下實(shí)例:
實(shí)例(Python 2.0+)
#!/usr/bin/python# -*- coding: UTF-8 -*-
total = 0 # 這是一個全局變量# 可寫函數(shù)說明def sum( arg1, arg2 ): ? #返回2個參數(shù)的和."
total = arg1 + arg2 # total在這里是局部變量.
print "函數(shù)內(nèi)是局部變量 : ", total
return total
#調(diào)用sum函數(shù)sum( 10, 20 )print "函數(shù)外是全局變量 : ", total
以上實(shí)例輸出結(jié)果:
函數(shù)內(nèi)是局部變量 : ?30函數(shù)外是全局變量 : ?0