Python學(xué)習(xí)筆記七:內(nèi)置函數(shù)補(bǔ)充闽烙,函數(shù)作用域蒸殿,閉包及遞歸

一、常見的內(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è)不同的命名空間)杜顺。

遞歸的核心:

  1. 遞歸推導(dǎo)式
  2. 遞歸終止條件

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鲁森、filterreduce。在較新的Python版本中振惰,函數(shù)mapfilter的用途并不大歌溉,應(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ù)(主要供mapfilterreduce使用)

>>> 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é)合使用reducelambda 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á)式中
??xlambda函數(shù)的一個(gè)參數(shù)缺亮,:為分割符翁涤,x>3為返回值,item for item in filterfilter函數(shù)的取值方式萌踱。

一般情況多考慮使用匿名函數(shù):

  • 程序一次性使用葵礼、不需要定義函數(shù)名時(shí),用匿名函數(shù)可以節(jié)省內(nèi)存中變量定義空間并鸵。
  • 如果想讓程序更加簡潔鸳粉,使用匿名函數(shù)就可以做到。

匿名函數(shù)有3個(gè)規(guī)則要記自暗!:

  1. 一般有一行表達(dá)式届谈,必須有返回值
  2. 不能有return
  3. 可以沒有參數(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]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扼仲,隨后出現(xiàn)的幾起案子远寸,更是在濱河造成了極大的恐慌,老刑警劉巖屠凶,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驰后,死亡現(xiàn)場離奇詭異,居然都是意外死亡矗愧,警方通過查閱死者的電腦和手機(jī)灶芝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唉韭,“玉大人夜涕,你說我怎么就攤上這事∈舴撸” “怎么了女器?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長住诸。 經(jīng)常有香客問我驾胆,道長涣澡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任丧诺,我火速辦了婚禮入桂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驳阎。我一直安慰自己抗愁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布搞隐。 她就那樣靜靜地躺著,像睡著了一般远搪。 火紅的嫁衣襯著肌膚如雪劣纲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天谁鳍,我揣著相機(jī)與錄音癞季,去河邊找鬼。 笑死倘潜,一個(gè)胖子當(dāng)著我的面吹牛绷柒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涮因,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼废睦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了养泡?” 一聲冷哼從身側(cè)響起嗜湃,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎澜掩,沒想到半個(gè)月后购披,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肩榕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年刚陡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片株汉。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筐乳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乔妈,到底是詐尸還是另有隱情哥童,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布褒翰,位于F島的核電站贮懈,受9級特大地震影響匀泊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜朵你,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一各聘、第九天 我趴在偏房一處隱蔽的房頂上張望抡医。 院中可真熱鬧躲因,春花似錦、人聲如沸忌傻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽水孩。三九已至镰矿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俘种,已是汗流浹背秤标。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宙刘,地道東北人苍姜。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像悬包,于是被迫代替她去往敵國和親衙猪。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容