一、常見的內(nèi)置函數(shù)
1. 查看內(nèi)置函數(shù):
>>> print(dir(__builtins__))
>>> li = dir(__builtins__)
>>> li.index('abs') #80
>>> li[80:]
['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
>>> len(li[80:]) #72
2. 常見函數(shù):
len 求長度
min 求最小值
max 求最大值
sorted 排序
reversed 反向
sum 求和
>>> help(sum) #求和一個(gè)可迭代對象鸣峭,start從哪開始宏所。
Help on built-in function sum in module builtins:
sum(iterable, start=0, /)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
When the iterable is empty, return the start value.
This function is intended specifically for use with numeric values and may
reject non-numeric types.
>>> sum([1,2,3,4]) 10
>>> sum([1,2,3,4],10) 20
3. 進(jìn)制轉(zhuǎn)換函數(shù):
函數(shù)名 | 描述 |
---|---|
bin() | 轉(zhuǎn)換為二進(jìn)制 |
oct() | 轉(zhuǎn)換為八進(jìn)制 |
hex() | 轉(zhuǎn)換為十六進(jìn)制 |
ord() | 將字符轉(zhuǎn)換成對應(yīng)的ASCII碼值 |
chr() | 將ASCII碼值轉(zhuǎn)換成對應(yīng)的字符 |
4. 補(bǔ)充:
(1) enumerate()
??enumerate(iterable[, start]) -> iterator for index, value of iterable
??返回一個(gè)可以枚舉的對象(一個(gè)元組(index, value))
>>> enumerate([1,2,3,4]) # <enumerate object at 0x00000193373611F8>
>>> list(enumerate([1,2,3,4])) #[(0, 1), (1, 2), (2, 3), (3, 4)]
>>> list(enumerate(['a','b','c'])) #[(0, 'a'), (1, 'b'), (2, 'c')]
>>> list(enumerate(['a','b','c'],5)) #[(5, 'a'), (6, 'b'), (7, 'c')]
>>> list(enumerate({'a','b','c'},5)) #[(5, 'a'), (6, 'c'), (7, 'b')]
>>> list(enumerate({'a':1,'b':1,'c':1},5)) #[(5, 'a'), (6, 'b'), (7, 'c')]
(2) filter()
??filter(function or None, iterable) --> filter object
??過濾器 將可迭代對象經(jīng)過函數(shù)的過濾再返回一個(gè)filter object
??參數(shù)(篩選函數(shù),篩選對象)
>>> filter(lambda x:x>2,[1,2,3,4,5])
#<filter object at 0x0000019337348EB8>
>>> list(filter(lambda x:x>2,[1,2,3,4,5]) # [3, 4, 5]
>>> list(filter(fun,[1,2,3,4,5]) #這里只是放一個(gè)函數(shù)體
>>> list(filter(None,[1,2,3,4,5])) #[1, 2, 3, 4, 5]
(3) map()
??<b><i>map(func, *iterables) --> map object</i></b>
??加工摊溶。
??對于參數(shù)iterable
中的每個(gè)元素都應(yīng)用fuction函數(shù)
爬骤,并返回一個(gè)map對象
>>> map(str,[1,2,3]) #<map object at 0x0000019337348D68>
>>> list(map(str,[1,2,3])) #['1', '2', '3']
(4) zip()
??zip(iter1 [,iter2 [...]]) --> zip object
??將對象逐一配對
>>> zip([1,2,3]) #<zip object at 0x000001933733F808>
>>> list(zip([1,2,3])) #[(1,), (2,), (3,)]
>>> list(zip([1,2,3],[11,22,33])) #[(1, 11), (2, 22), (3, 33)]
>>> list(zip([1,2,3],[11,22,33],'a')) #[(1, 11, 'a')]后面兩個(gè)沒有配了
>>> list(zip([1,2,3],[11,22,33],'aaaaa'))
#[(1, 11, 'a'), (2, 22, 'a'), (3, 33, 'a')]
??配對的參數(shù)可多不可少
二、作用域
??變量到底是什么呢莫换?可將其視為指向值的名稱霞玄。因此骤铃,執(zhí)行賦值語句x=1
后,名稱x
指向值1
坷剧。這幾乎與使用字典一樣(字典中的鍵指向值)惰爬,只是你使用的是"看不見"的字典。實(shí)際上惫企,這種解釋已經(jīng)離真相不遠(yuǎn)撕瞧。有一個(gè)名為vars
的內(nèi)置函數(shù),它返回這個(gè)不可見的字典:
>>> x = 1
>>> scope = vars()
>>> scope['x'] #1
??警告:一般而言狞尔,不應(yīng)修改vars返回的字典丛版,因?yàn)楦鶕?jù)Python官方文檔的說法,這樣做的結(jié)果是不確定的偏序。
??這種"看不見的字典"稱為命名空間或作用域页畦。在Python中,程序的變量并不是在任何位置都可以訪問的研儒,訪問權(quán)限決定于這個(gè)變量是在哪里賦值的豫缨,代碼中變量被賦值的位置決定哪些范圍的對象可以訪問這個(gè)變量,這個(gè)范圍就是命名空間端朵。
??那么有多少個(gè)命名空間呢州胳?除全局作用域外,每個(gè)函數(shù)調(diào)用都將創(chuàng)建一個(gè)逸月,函數(shù)中定義的變量等可以認(rèn)為都是存儲(chǔ)在這個(gè)命名空間中的,這些變量的調(diào)用不會(huì)影響到全局變量遍膜。
1. 局部變量與全局變量
??變量的作用域決定哪一部分程序可以訪問特定的變量名稱碗硬。
>>> x=1 #全局
>>> def fun1():
y = 2 # 局部
print(x,y)
>>> fun1() #1 2
??全局能夠進(jìn)入局部變量。
>>> x = 1
>>> def foo(): x = 42
>>> foo()
>>> x #1
??在這里瓢颅,函數(shù)foo
修改了變量x恩尾,但當(dāng)你最終查看時(shí),他根本沒變挽懦。這是因?yàn)檎{(diào)用foo
時(shí)創(chuàng)建了一個(gè)新的命名空間翰意,供foo
中的代碼塊使用。賦值語句x=42
是在這個(gè)內(nèi)部作用域(局部命名空間)中執(zhí)行的信柿,不影響外部(全局)作用域內(nèi)的x
冀偶。
??在函數(shù)內(nèi)使用的變量只能被函數(shù)內(nèi)部引用,不能再函數(shù)外引用渔嚷,這個(gè)變量的作用域是局部的进鸠,也稱為局部變量。
??在函數(shù)外形病,一段代碼最開始賦值的變量可以被多個(gè)函數(shù)引用客年,這就是全局變量霞幅。
??全局變量可以在整個(gè)程序范圍內(nèi)訪問。參數(shù)類似于局部變量量瓜,因此參數(shù)與全局變量同名不會(huì)有任何問題司恳。
??函數(shù)中使用某個(gè)變量時(shí),如果參數(shù)中的局部變量與全局變量同名绍傲,默認(rèn)使用局部變量扔傅。
??如果只是想讀取這種變量的值(不去重新關(guān)聯(lián)它),通常不會(huì)有任何問題唧取。
>>> def combine(parameter): print(parameter + external)
>>> external = 'berry'
>>> combine('Shurb') #Shrubbery
??警告:像這樣訪問全局變量是眾多bug的根源铅鲤。務(wù)必慎用全局變量。
??遮蓋問題:
??如果有一個(gè)局部變量或參數(shù)與你要訪問的全局變量同名枫弟,就無法直接訪問全局變量邢享,因?yàn)樗痪植孔兞空谧×恕?br>
??例如,在前面的示例中淡诗,如果有一個(gè)名為parameter
的全局變量骇塘,就無法在函數(shù)combine
中訪問它,因?yàn)橛幸粋€(gè)與之同名的參數(shù)韩容。然而款违,必要時(shí)可使用globals()['parameter']
來訪問它。
>>> def combine(parameter): print(parameter + globals()['parameter'])
>>> external = 'berry'
>>> combine('Shurb') #Shrubbery
??重新關(guān)聯(lián)全局變量(使其指向新值)是另一碼事群凶。在函數(shù)內(nèi)部給變量賦值時(shí)插爹,該變量默認(rèn)為局部變量,除非你明確告訴Python它是全局變量请梢。那么如何告知呢赠尾?
??如果你想指明使用全局變量,可以使用globals()['全局變量名']
毅弧,或者global 變量名
气嫁。這個(gè)函數(shù)返回一個(gè)包含全局變量的字典。(locals
返回一個(gè)包含局部變量的字典够坐。)
>>> def fun2():
a = 1
print(a)
>>> a #NameError: name 'a' is not defined
>>> fun2() #1
>>> a #NameError: name 'a' is not defined
??局部變量不能進(jìn)入全局,
>>> x = 1
>>> def change_global():
global x
x = x + 1
>>> change_global()
>>> x #2
??如果要進(jìn)寸宵,也要global
。
2. 作用域嵌套
??另外元咙,Python是支持函數(shù)嵌套使用的,即可將一個(gè)函數(shù)放在另一個(gè)函數(shù)內(nèi)梯影,如下所示:
>>> def foo():
def bar():
print('hello')
bar()
??嵌套的用處不大,但有一個(gè)很突出的用途庶香,使用一個(gè)函數(shù)來創(chuàng)建另一個(gè)函數(shù)光酣。這意味著可像下面這樣編寫函數(shù):
>>> def multiplier(factor):
def multiplyByFactor(number):
return number * factor
return multiplyByFactor #返回函數(shù)體
>>> multiplier()
# <function multiplier.<locals>.multiplyByFactor at 0x0000019337373E18>
??在這里,一個(gè)函數(shù)位于另一個(gè)函數(shù)中脉课,且外面的函數(shù)返回里層的函數(shù)救军。也就是返回一個(gè)函數(shù)體财异,而不是調(diào)用它。重要的是唱遭,返回的函數(shù)能夠訪問其定義所在的作用域戳寸。換而言之,它攜帶著自己所在的環(huán)境(和相關(guān)的局部變量)拷泽。
??每當(dāng)外部函數(shù)被調(diào)用時(shí)疫鹊,都將重新定義內(nèi)部的函數(shù),而變量factor
的值也可能不同司致。由于Python的嵌套作用域拆吆,可在內(nèi)部函數(shù)中訪問這個(gè)來自外部局部作用域(multiplier)的變量,如下所示:
>>> double = multiplier(2)
>>> double(5) #10
>>> trible = multiplier(3)
>>> trible(3) #9
>>> multiplier(5)(4) #20
??像multiplyByFactor
這樣存儲(chǔ)其所在作用域的函數(shù)成為閉包脂矫。倆個(gè)函數(shù) 嵌套枣耀。
>>> def test1():
a = 1
print('局部外層')
# test2()不能放在這里,因?yàn)楹瘮?shù)還沒有定義
def test2():
b = 2
a += 1
print('局部內(nèi)層', a,b)
test2()
??打印一行庭再,然后報(bào)錯(cuò)捞奕。局部外層能進(jìn)入局部里層,但是不能修改。通常拄轻,不能給外部作用域內(nèi)的變量賦值颅围。
>>> def test1():
a = 1
print('局部外層')
# test2()不能放在這里,因?yàn)楹瘮?shù)還沒有定義
def test2():
b = 2
nonlocal a
a += 1
print('局部內(nèi)層', a,b)
test2()
??但如果一定要這樣做恨搓,可使用關(guān)鍵字nonlocal
院促。這個(gè)關(guān)鍵字的用法和global
很像,讓你能夠給外部作用域(非全局作用域)內(nèi)的變量賦值斧抱。(作用于局部)常拓。
??使用global
情況:
- 全局變量可以在函數(shù)內(nèi)部訪問,但是不能改變
- 如果在函數(shù)內(nèi)部想修改全局變量夺姑,可以用
global
來修飾變量 - 局部變量只能在局部進(jìn)行訪問修改。
- 如果在函數(shù)外部掌猛,想訪問局部變量盏浙,也可以用
global
,將局部變量聲明為全局變量
??使用nonlocal
的情況:
- 當(dāng)里層局部荔茬,需要修改外層局部時(shí)废膘,需要使用
nonlocal
。 (如嵌套函數(shù))
3. 回調(diào)函數(shù)
??倆個(gè)函數(shù) 不嵌套
>>> def test12():
print('我是第一個(gè)函數(shù)')
>>> def fun12(a):
a()
print('我是老二')
>>> fun(test1)
# 我是第一個(gè)函數(shù)
# 我是老二
??閉包加回調(diào)函數(shù)組成裝飾器慕蔚。
三丐黄、遞歸
??遞歸意味著引用自身,即自己調(diào)用自己孔飒。例如:
>>> def recursion():
return recursion()
??這個(gè)函數(shù)中的遞歸稱為無窮遞歸灌闺,因?yàn)樗鼜睦碚撋险f永遠(yuǎn)不會(huì)結(jié)束艰争。這類遞歸稱作無窮遞歸,實(shí)際操作一會(huì)兒程序就崩潰了桂对。因?yàn)槊看握{(diào)用函數(shù)都會(huì)用掉一點(diǎn)內(nèi)存甩卓,當(dāng)內(nèi)存空間被占滿,程序就會(huì)報(bào)異常蕉斜。
??如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身逾柿,這個(gè)函數(shù)就稱作遞歸函數(shù)。
??有用的遞歸函數(shù)通常包含下面兩部分:
- 基線條件(針對最小的問題):滿足這種條件時(shí)函數(shù)將直接返回一個(gè)值宅此。(這樣就避免了無限調(diào)用的可能)
- 遞歸條件:包含一個(gè)或多個(gè)調(diào)用机错,這些調(diào)用旨在解決問題的一部分。
??這里的關(guān)鍵是父腕,通過將問題分解為較小的部分弱匪,可避免遞歸沒完沒了,因?yàn)閱栴}終將被分解成基線條件可以解決的最小問題侣诵。
??其實(shí)函數(shù)每次被調(diào)用時(shí)都會(huì)創(chuàng)建一個(gè)新命名空間痢法,也就是當(dāng)函數(shù)調(diào)用自身時(shí),實(shí)際上運(yùn)行的是兩個(gè)不同的函數(shù)(也可以說一個(gè)函數(shù)具有兩個(gè)不同的命名空間)杜顺。
遞歸的核心:
- 遞歸推導(dǎo)式
- 遞歸終止條件
1. 兩個(gè)經(jīng)典案例:階乘和冪
??階乘:當(dāng)然财搁,你可以用循環(huán)的思想來寫,像下面這樣
>>> def factorial(n):
result = n
for i in range(1,n):
result *= i
return result
??它是這樣做的:首先將result
設(shè)置為n
躬络,再將其依次乘以1到n-1的每個(gè)數(shù)字尖奔,最后返回result
。關(guān)于階乘的數(shù)學(xué)定義為:1的階乘為1穷当。對于大于1的數(shù)字n其階乘為n-1
的階乘再乘以n
提茁。
這里我們換一種思路,用遞歸來實(shí)現(xiàn):
>>> def factorial(n):
if n == 1: #基線條件馁菜,滿足即退出函數(shù)
return 1
else:
return n * factorial(n – 1)
??我們再來定義冪的運(yùn)算(就是和內(nèi)置函數(shù)pow
一樣的效果)茴扁。冪運(yùn)算的定義是power(x,n)
(x的n次冪)是將數(shù)字x自乘n-1次的結(jié)果,即將n個(gè)x相乘的結(jié)果汪疮。
>>> def power(x, n):
result = 1
for i in range(n):
result *= x
return result
??遞歸式定義為對于任何數(shù)字x峭火,power(x,0)都為1。n>0時(shí)智嚷,power(x,n)為power(x,n-1)與x的乘積卖丸。
>>> def power(x, n):
if n == 0:
return 1
else:
return x * power(x, n-1)
??當(dāng)然,你可以明顯的看到盏道,遞歸大部分情況是可以用循環(huán)代替的稍浆,而且循環(huán)在時(shí)間復(fù)雜度可能更好一點(diǎn),但是當(dāng)你掌握了遞歸,你就會(huì)愛上這種簡潔的表達(dá)方式衅枫。
??提示:如果函數(shù)或算法復(fù)雜難懂嫁艇,在實(shí)現(xiàn)前用自己的話進(jìn)行明確的定義將大有裨益。以這種"準(zhǔn)編程語言"編寫的程序通常稱為偽代碼为鳄。
??在大多數(shù)情況下裳仆,使用循環(huán)的效率可能更高。然而孤钦,在很多情況下歧斟,使用遞歸的可讀性更高,且有時(shí)要高得多偏形。遞歸函數(shù)的優(yōu)點(diǎn)是定義簡單静袖,邏輯清晰。
>>> def fact(n):
if n == 1:
return 1
return n * fact(n – 1)
??使用遞歸函數(shù)需要注意防止棧溢出俊扭。在計(jì)算機(jī)中队橙,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的。每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用萨惑,棧就會(huì)加一層棧幀捐康;每當(dāng)函數(shù)返回,棧就會(huì)減一層棧幀庸蔼。由于棧的大小不是無限的解总,因此遞歸調(diào)用的次數(shù)過多會(huì)導(dǎo)致棧溢出。
>>> fact(1000)
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
fact(1000)
File "<pyshell#13>", line 4, in fact
return n * fact(n-1)
File "<pyshell#13>", line 4, in fact
return n * fact(n-1)
File "<pyshell#13>", line 4, in fact
return n * fact(n-1)
[Previous line repeated 989 more times]
File "<pyshell#13>", line 2, in fact
if n == 1:
RecursionError: maximum recursion depth exceeded in comparison
??異常提示超過最大遞歸深度姐仅。
??解決遞歸調(diào)用棧溢出的方法是通過尾遞歸優(yōu)化花枫,事實(shí)上尾遞歸和循環(huán)的效果一樣,把循環(huán)看成是一種特殊尾遞歸函數(shù)也可以掏膏。
??尾遞歸是指在函數(shù)返回時(shí)調(diào)用函數(shù)本身劳翰,并且return
語句不能包含表達(dá)式。這樣馒疹,編譯器或解釋器就可以對尾遞歸進(jìn)行優(yōu)化佳簸,使遞歸本身無論調(diào)用多少次都只占用一個(gè)棧幀,從而避免棧溢出的情況颖变。
>>> def fact(n):
return fact_iter(n,1)
>>> def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
??可以看到生均,return fact_iter(num - 1, num * product)
僅返回遞歸函數(shù)本身, num - 1和num * product
在函數(shù)調(diào)用前就會(huì)被計(jì)算悼做,不影響函數(shù)調(diào)用疯特。
??由操作結(jié)果看到哗魂,調(diào)用尾遞歸時(shí)如果做了優(yōu)化肛走,棧就不會(huì)增長,因此無論多少次調(diào)用都不會(huì)導(dǎo)致棧溢出录别。
2. 另一個(gè)經(jīng)典案例:二分查找
??例如朽色,對方心里想著一個(gè)1-100的數(shù)字谭期,你必須猜出是哪個(gè)老玛。實(shí)際上只需要猜7次。首先問:這個(gè)數(shù)字大于50嗎?如果答案是肯定的如捅,再問:這個(gè)數(shù)字大于75嗎?不斷將可能的區(qū)間減半挑社,知道猜對為止医寿。你無需過多地思考就能成功。
??這里的關(guān)鍵是盈咳,這種算法自然而然地引出了遞歸式定義和實(shí)現(xiàn)耿眉。先來回顧一下定義,確保知道該如何做鱼响。
- 如果上限和下限相同鸣剪,就說明它們都指向數(shù)字所在的位置,因此將這個(gè)數(shù)字返回丈积。
- 否則筐骇,找出區(qū)間的中間位置(上限和下限的平均值),再確定數(shù)字在左半部分還是有半部分江滨。然后繼續(xù)在數(shù)字所在的那部分中查找铛纬。
??在這個(gè)遞歸案例中,關(guān)鍵在于元素是經(jīng)過排序的牙寞。找出中間的元素后饺鹃,只需將其與要查找的數(shù)字進(jìn)行比較即可。如果要查找的數(shù)字更大间雀,肯定在右邊悔详;如果更小,它必然在左邊惹挟。遞歸部分為"繼續(xù)在數(shù)字所在的那部分中查找"茄螃,因?yàn)椴檎曳绞脚c定義所指定的完全相同。(請注意连锯,這種查找算法返回?cái)?shù)字應(yīng)該在的位置归苍。如果這個(gè)數(shù)字不在序列中,那么這個(gè)位置上的自然是另一個(gè)數(shù)字运怖。)
??現(xiàn)在可以實(shí)現(xiàn)二分查找了拼弃。
>>> def search(sequence, number, lower=0, upper=None):
if upper is None: upper = len(sequence) - 1
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)
??提示:實(shí)際上,模塊bisect
提供了標(biāo)準(zhǔn)的二分查找實(shí)現(xiàn)摇展。
3. 函數(shù)式編程
??在Python中吻氧,通常不會(huì)如此倚重函數(shù)(而是創(chuàng)建自定義對象,這將在下一章詳細(xì)介紹),但完全可以這樣做盯孙。
??Python提供了一些有助于這種函數(shù)式編程的函數(shù):map
鲁森、filter
和reduce
。在較新的Python版本中振惰,函數(shù)map
和filter
的用途并不大歌溉,應(yīng)該使用列表推導(dǎo)來替代它們。你可使用map將序列的所有元素傳遞給函數(shù)骑晶。
函數(shù)名 | 描述 |
---|---|
map(func, seq[,seq,…]) | 對序列中的所有元素執(zhí)行函數(shù) |
filter(func,seq) | 返回一個(gè)列表痛垛,其中包含對其執(zhí)行函數(shù)時(shí)結(jié)果為真的所有函數(shù) |
reduce(func,seq[,initial]) | 等價(jià)于func(func(func(seq[0],seq[1]),seq[2]),…) |
sum(seq) | 返回seq中所有元素的和 |
apply(func[,args[,kwargs]]) | 調(diào)用函數(shù)(還提供要傳遞給函數(shù)的參數(shù)) |
>>> list(map(str, range(10))) #與[str(i)for i in range(10)]等價(jià)
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
??你可使用filter
根據(jù)布爾函數(shù)的返回值對元素進(jìn)行過濾。
>>> def func(x):
return x.isalnum()
>>> seq = ["foo", "x41", "?!", "***"]
>>> list(filter(func, seq)) #['foo', 'x41']
??就這個(gè)示例而言桶蛔,如果轉(zhuǎn)而使用列表推導(dǎo)榜晦,就無需創(chuàng)建前述自定義函數(shù)。
>>> [x for x in seq if x.isalnum()] #['foo', 'x41']
??實(shí)際上羽圃,Python提供了一種名為lambda
表達(dá)式的功能乾胶,讓你能夠創(chuàng)建內(nèi)嵌的簡單函數(shù)(主要供map
、filter
和reduce
使用)
>>> filter(lambda x: x.isalnum(), seq) #['foo', 'x41']
??然而朽寞,使用列表推導(dǎo)的可讀性不是更高嗎识窿?
??要使用列表推導(dǎo)來替換函數(shù)reduce
不那么容易,而這個(gè)函數(shù)提供的功能即便能用到脑融,也用的不多喻频。它使用指定的函數(shù)將序列的前兩個(gè)元素合二為一,再將結(jié)果與第3個(gè)元素合二為一肘迎,以此類推甥温,直到處理完整個(gè)序列并得到一個(gè)結(jié)果。例如妓布,如果你要將序列中的所有數(shù)相加姻蚓,可結(jié)合使用reduce
和lambda x,y:x+y
。
>>> numbers = [1,2,3,4]
>>> from functools import reduce
>>> reduce(lambda x,y: x+y,numbers) #10
??就這個(gè)示例而言匣沼,還不如使用內(nèi)置函數(shù)sum
狰挡。
四、匿名函數(shù)
??匿名函數(shù)就是不再使用def語句這樣的標(biāo)準(zhǔn)形式定義一個(gè)函數(shù)释涛。
??Python使用lambda
創(chuàng)建匿名函數(shù)加叁。lambda
只是表達(dá)式,函數(shù)體比def
簡單很多唇撬。
??lambda
的主體是一個(gè)表達(dá)式它匕,而不是一個(gè)代碼塊,僅能在lambda
表達(dá)式中封裝優(yōu)先的邏輯窖认。??lambda
函數(shù)擁有自己的命名空間豫柬,不能訪問自有參數(shù)列表之外或全局命名空間的參數(shù)愈污。
??lambda
函數(shù)的語法只包含一個(gè)語句:lambda [args1[,args2,…argn]]:expression
??看一個(gè)求兩個(gè)數(shù)和的示例。
>>> def func(x,y):
return x+y
>>> lambda x,y:x+y
??可以看出轮傍,使用lambda
表達(dá)編寫的代碼比使用def
語句少。
??比如求一個(gè)列表中大于3的元素首装,通過函數(shù)式編程實(shí)現(xiàn)创夜,運(yùn)用filter
。
>>> def func(x):
return x>3
>>> f_list = filter(func,[1,2,3,4,5])
>>> print([item for item in f_list])
??如果使用匿名函數(shù)仙逻,
>>> print([item for item in filter(lambda x:x>3,[1,2,3,4,5])
??從上面的操作可以看出驰吓,lambda
一般應(yīng)用于函數(shù)式編程,代碼簡介系奉,常和filter
等函數(shù)結(jié)合使用檬贰。
??我們對lambda
進(jìn)行解析。在表達(dá)式中
??x
為lambda
函數(shù)的一個(gè)參數(shù)缺亮,:
為分割符翁涤,x>3
為返回值,item for item in filter
是filter
函數(shù)的取值方式萌踱。
一般情況多考慮使用匿名函數(shù):
- 程序一次性使用葵礼、不需要定義函數(shù)名時(shí),用匿名函數(shù)可以節(jié)省內(nèi)存中變量定義空間并鸵。
- 如果想讓程序更加簡潔鸳粉,使用匿名函數(shù)就可以做到。
匿名函數(shù)有3個(gè)規(guī)則要記自暗!:
- 一般有一行表達(dá)式届谈,必須有返回值
- 不能有return
- 可以沒有參數(shù),也可以有一個(gè)或多個(gè)參數(shù)
??下面來看幾個(gè)匿名函數(shù)的示例弯汰。
無參匿名函數(shù):
>>> t = lambda :True
>>> t() #True
帶參數(shù)匿名函數(shù)
>>> lambda x : x**3
>>> lambda x,y,z : x+y+z
>>> lambda x,y=3 : x*y
匿名函數(shù)調(diào)用:
>>> c = lambda x,y,z : x*y*z
>>> c(2,3,4) #24
五艰山、偏函數(shù)
??偏函數(shù)通過模塊functools
被用戶調(diào)用。
??偏函數(shù)是將所要承載的函數(shù)作為partial()函數(shù)
的第一個(gè)參數(shù)咏闪,原函數(shù)的各個(gè)參數(shù)一次作為partial()函數(shù)
的后續(xù)參數(shù)程剥,除非使用關(guān)鍵字參數(shù)。
??在這個(gè)例子里汤踏,將實(shí)現(xiàn)一個(gè)取余函數(shù)织鲸,取得整數(shù)100對不同數(shù)m的100%m的余數(shù)。
>>> from functools import partial
>>> def mod(n,m):
return n%m
>>> mod_by_100 = partial(mod,100)
>>> print(mod(100,7) #2
>>> print(mod_by_100(7)) #2
??由執(zhí)行結(jié)果看到溪胶,使用偏函數(shù)所需代碼量比自定義函數(shù)更少搂擦、更簡潔。
六哗脖、快速排序
??快速排序是一種分治排序算法瀑踢。該算法首先選取一個(gè)劃分元素(pivot)扳还;然后重排列表,將其劃分為3個(gè)部分橱夭,即left(小于劃分元素pivot的部分)氨距,pivot、right(大于劃分元素pivot的部分)棘劣,此時(shí)劃分元素pivot已經(jīng)在列表的最終位置上俏让;最后分別對left和right兩部分進(jìn)行遞歸排序。
??其中茬暇,劃分元素的選取直接影響快速排序算法的效率首昔,通常選擇列表的第一個(gè)元素、中間元素或最后一個(gè)元素作為劃分元素糙俗,當(dāng)然也有更復(fù)雜的選擇方式勒奇。劃分過程根據(jù)劃分元素重排列表,是快速排序算法的關(guān)鍵所在巧骚。
??快速排序算法的優(yōu)點(diǎn)是原位排序(只使用很小的輔助棧)赊颠,平均時(shí)間復(fù)雜度為O(n log n)。快速排序算法的缺點(diǎn)是不穩(wěn)定劈彪,最壞情況下時(shí)間復(fù)雜度為O(n2)
>>> def quicksort(L):
qsort(L, 0, len(L) - 1)
>>> def qsort(L, first, last):
if first < last:
split = partition(L, first, last)
qsort(L, first, split - 1)
qsort(L, split + 1, last)
>>> def partition(L, first, last):
# 選取列表中的第一個(gè)元素作為劃分元素
pivot = L[first]
leftmark = first + 1
rightmark = last
while True:
while L[leftmark] <= pivot:
# 如果列表中存在與劃分元素相等的元素巨税,讓它位于left部分
# 以下檢測用于劃分元素pivot是列表中的最大元素時(shí)
# 放置leftmark越界
if leftmark == rightmark:
break
leftmark += 1
while L[rightmark] > pivot:
# 這里不需要檢測,劃分元素pivot是列表中的最小元素時(shí)
# rightmark自動(dòng)停在first處
rightmark -= 1
if leftmark < rightmark:
# 此時(shí)粉臊,leftmark處的元素大于pivot
# rightmark處的元素小于等于pivot草添,交換兩者
L[leftmark], L[rightmark] = L[rightmark], L[leftmark]
else:
break
# 交換first處的劃分元素與rightmark處的元素
L[first], L[rightmark] = L[rightmark], L[first]
# 返回劃分元素pivot的最終位置
return rightmark
>>> num_list = [5,-4,6,3,7,11,1,2]
>>> quicksort(num_list)
>>> print(num_list) #[-4, 1, 2, 3, 5, 7, 6, 11]