那現(xiàn)在我們將看到怎樣使用*args和**kwargs 來(lái)調(diào)用一個(gè)函數(shù)。 假設(shè),你有這樣一個(gè)小函數(shù):
def test_args_kwargs(arg1, arg2, arg3):
??? print("arg1:", arg1)
??? print("arg2:", arg2)
??? print("arg3:", arg3)
你可以使用*args或**kwargs來(lái)給這個(gè)小函數(shù)傳遞參數(shù)。 下面是怎樣做:
# 首先使用 *args
args = ("two", 3, 5)
test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
# 現(xiàn)在使用 **kwargs:
kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
標(biāo)準(zhǔn)參數(shù)與*args、**kwargs在使用時(shí)的順序
那么如果你想在函數(shù)里同時(shí)使用所有這三種參數(shù), 順序是這樣的:
some_func(fargs, *args, **kwargs)
這還真的要看你的需求而定。
最常見(jiàn)的用例是在寫(xiě)函數(shù)裝飾器的時(shí)候(會(huì)在另一章里討論)。
此外它也可以用來(lái)做猴子補(bǔ)丁(monkey patching)。猴子補(bǔ)丁的意思是在程序運(yùn)行時(shí)(runtime)修改某些代碼。
打個(gè)比方,你有一個(gè)類(lèi),里面有個(gè)叫g(shù)et_info的函數(shù)會(huì)調(diào)用一個(gè)API并返回相應(yīng)的數(shù)據(jù)。如果我們想測(cè)試它,可以把API調(diào)用替換成一些測(cè)試數(shù)據(jù)。例
如:
import someclass
def get_info(self, *args):
??? return "Test data"
someclass.get_info = get_info
我敢肯定你也可以想象到一些其他的用例。
首先我們要理解迭代器(iterators)。根據(jù)維基百科,迭代器是一個(gè)讓程序員可以遍歷一個(gè)容器(特別是列表)的對(duì)象。然而,一個(gè)迭代器在遍歷并讀取一個(gè)容器的數(shù)據(jù)元素時(shí),并不會(huì)執(zhí)行一個(gè)迭代。你可能有點(diǎn)暈了,那我們來(lái)個(gè)慢動(dòng)作。換句話說(shuō)這里有三個(gè)部分:
可迭代對(duì)象(Iterable)
迭代器(Iterator)
迭代(Iteration)
上面這些部分互相聯(lián)系。我們會(huì)先各個(gè)擊破來(lái)討論他們,然后再討論生成器(generators).
Python中任意的對(duì)象,只要它定義了可以返回一個(gè)迭代器的__iter__方法,或者定義了可以支持下標(biāo)索引的__getitem__方法(這些雙下劃線方法會(huì)在其他章節(jié)中全面解釋),那么它就是一個(gè)可迭代對(duì)象。簡(jiǎn)單說(shuō),可迭代對(duì)象就是能提供迭代器的任意對(duì)象。那迭代器又是什么呢?
任意對(duì)象,只要定義了next(Python2) 或者_(dá)_next__方法,它就是一個(gè)迭代器。就這么簡(jiǎn)單?,F(xiàn)在我們來(lái)理解迭代(iteration)
用簡(jiǎn)單的話講,它就是從某個(gè)地方如一個(gè)列表)取出一個(gè)元素的過(guò)程。當(dāng)我們使用一個(gè)循環(huán)來(lái)遍歷某個(gè)東西時(shí),這個(gè)過(guò)程本身就叫迭代?,F(xiàn)在既然我們有了這些術(shù)語(yǔ)的基本理解,那我們開(kāi)始理解生成器吧。
生成器也是一種迭代器,但是你只能對(duì)其迭代一次。這是因?yàn)樗鼈儾](méi)有把所有的值存在內(nèi)存中,而是在運(yùn)行時(shí)生成值。你通過(guò)遍歷來(lái)使用它們,要么用一個(gè)“for”循環(huán),要么將它們傳遞給任意可以進(jìn)行迭代的函數(shù)和結(jié)構(gòu)。大多數(shù)時(shí)候生成器是以函數(shù)來(lái)實(shí)現(xiàn)的。然而,它們并不返回一個(gè)值,而是yield(暫且譯作“生出”)一個(gè)值。這里有個(gè)生成器函數(shù)的簡(jiǎn)單例子:
def generator_function():
??? for i in range(10):
??????? yield i
for item in generator_function():
??? print(item)
這個(gè)案例并不是非常實(shí)用。生成器最佳應(yīng)用場(chǎng)景是:你不想同一時(shí)間將所有計(jì)算出來(lái)的大量結(jié)果集分配到內(nèi)存當(dāng)中,特別是結(jié)果集里還包含循環(huán)。
譯者注:這樣做會(huì)消耗大量資源
許多Python 2里的標(biāo)準(zhǔn)庫(kù)函數(shù)都會(huì)返回列表,而Python 3都修改成了返回生成器,因?yàn)樯善髡加酶俚馁Y源。
下面是一個(gè)計(jì)算斐波那契數(shù)列的生成器:
# generator version
def fibon(n):
??? a = b = 1
??? for i in range(n):
??????? yield a
??????? a, b = b, a + b
# Now we can use it like this
for x in fibon(1000000):
??? print(x)
用這種方式,我們可以不用擔(dān)心它會(huì)使用大量資源。然而,之前如果我們這樣來(lái)實(shí)現(xiàn)的話:
def fibon(n):
??? a = b = 1
??? result = []
??? for i in range(n):
??????? result.append(a)
??????? a, b = b, a + b
??? return result
這也許會(huì)在計(jì)算很大的輸入?yún)?shù)時(shí),用盡所有的資源。我們已經(jīng)討論過(guò)生成器使用一次迭代,但我們并沒(méi)有測(cè)試過(guò)。在測(cè)試前你需要再知道一個(gè)Python內(nèi)置函數(shù):next()。它允許我們獲取一個(gè)序列的下一個(gè)元素。那我們來(lái)驗(yàn)證下我們的理解:
def generator_function():
??? for i in range(3):
??????? yield i
gen = generator_function()
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
#?????????? File "<stdin>", line 1, in <module>
#?????????? StopIteration
我們可以看到,在yield掉所有的值后,next()觸發(fā)了一個(gè)StopIteration的異常?;旧线@個(gè)異常告訴我們,所有的值都已經(jīng)被yield完了。你也許會(huì)奇怪,為什么我們?cè)谑褂胒or循環(huán)時(shí)沒(méi)有這個(gè)異常呢?啊哈,答案很簡(jiǎn)單。for循環(huán)會(huì)自動(dòng)捕捉到這個(gè)異常并停止調(diào)用next()。你知不知道Python中一些內(nèi)置數(shù)據(jù)類(lèi)型也支持迭代哦?我們這就去看看:
my_string = "Yasoob"
next(my_string)
# Output: Traceback (most recent call last):
#?????? File "<stdin>", line 1, in <module>
#?????? TypeError: str object is not an iterator
好吧,這不是我們預(yù)期的。這個(gè)異常說(shuō)那個(gè)str對(duì)象不是一個(gè)迭代器。對(duì),就是這樣!它是一個(gè)可迭代對(duì)象,而不是一個(gè)迭代器。這意味著它支持迭代,但我們不能直接對(duì)其進(jìn)行迭代操作。那我們?cè)鯓硬拍軐?duì)它實(shí)施迭代呢?是時(shí)候?qū)W習(xí)下另一個(gè)內(nèi)置函數(shù),iter。它將根據(jù)一個(gè)可迭代對(duì)象返回一個(gè)迭代器對(duì)象。這里是我們?nèi)绾问褂盟?#xff1a;
my_string = "Yasoob"
my_iter = iter(my_string)
next(my_iter)
# Output: 'Y'
現(xiàn)在好多啦。我肯定你已經(jīng)愛(ài)上了學(xué)習(xí)生成器。一定要記住,想要完全掌握這個(gè)概念,你只有使用它。確保你按照這個(gè)模式,并在生成器對(duì)你有意義的任何時(shí)候都使用它。你絕對(duì)不會(huì)失望的!
聯(lián)系客服