Python函數詳解

函數是Python裏組織代碼的最小單元,Python函數包含如下幾個部分:html

  • 定義函數
  • 調用函數
  • 參數
  • 函數的返回值
  • 函數的嵌套
  • 做用域
  • 函數執行流程
  • 遞歸函數
  • 匿名函數
  • 生成器
  • 高階函數

<!--more-->python

定義函數

def add(x, y):     # 函數定義 def 表示定義一個函數, 緊接着是函數名 函數名後面用一對小括號列出參數列表,參數列表後面使用一個冒號開始函數體
    print(x + y)   # 函數體是正常的Python語句,能夠包含任意結構
    return  x + y  # return 語句表示函數的返回值

函數是有輸入(參數)和輸出(返回值)的代碼單元, 把輸入轉化爲輸出程序員

調用函數

定義函數的時候,並不會執行函數體, 當調用函數的時候,纔會執行其中的語句塊segmentfault

In [1]: def add(x, y):     # 函數定義 def 表示定義一個函數, 緊接着是函數名 函數名後面用一對小括號
   ...:         print(x + y)   # 函數體是正常的Python語句,能夠包含任意結構
   ...:         return  x + y  # return 語句表示函數的返回值
   ...: 

In [2]: add(3, 5) # 函數使用函數名來調用,函數名後緊跟一對小括號,小括號裏傳入函數定義時的參數
8
Out[2]: 8

In [3]: add(3, 4, 5) # 傳入參數必須和函數定義時的參數相匹配,若是不匹配,會拋出TypeError
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-a11d83d1db7e> in <module>()
----> 1 add(3, 4, 5)

TypeError: add() takes 2 positional arguments but 3 were given

參數

傳參方式

In [5]: def add(x, y):
   ...:     ret = x + y
   ...:     print('{} + {} = {}'.format(x, y, x+y))
   ...:     return ret
   ...: 

In [6]: add(3, 5) #參數按照定義的順序傳入,這樣的傳參方法叫作位置參數
3 + 5 = 8
Out[6]: 8

In [7]: add(y=3, x=5) #參數按照定義時的變量名傳遞,這樣的傳參方法叫作關鍵字參數,關鍵字參數和順序無關
5 + 3 = 8
Out[7]: 8

In [8]: add(5, y=3) # 位置參數和關鍵字參數能夠混用
5 + 3 = 8
Out[8]: 8

In [9]: add(x=3, 5)    # 位置參數不能放在關鍵字參數的後面
  File "<ipython-input-9-165b39de39ac>", line 1
    add(x=3, 5)
            ^
SyntaxError: positional argument follows keyword argument


In [10]: add('3', '5')    # python是動態語言,傳入的參數類型能夠不固定
3 + 5 = 35
Out[10]: '35'

In [11]: add(3, '5') # python是強類型語言,傳入的參數須要知足強類型要求,不然會拋出TypeError
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-335767c130e1> in <module>()
----> 1 add(3, '5')

<ipython-input-5-e720706d1634> in add(x, y)
      1 def add(x, y):
----> 2     ret = x + y
      3     print('{} + {} = {}'.format(x, y, x+y))
      4     return ret

TypeError: unsupported operand type(s) for +: 'int' and 'str'

參數默認值

參數能夠有默認值,當一個參數有默認值時, 調用時若是不傳遞此參數,會使用默認值閉包

In [12]: def inc(x, y=1):    # 參數y默認爲1
    ...:     x += y
    ...:     return x
    ...: 

In [13]: inc(3)    # 傳參時只須要傳入x便可
Out[13]: 4

In [14]: inc(3, 2)
Out[14]: 5

In [15]: def inc(x=1, y):    # 默認參數不能再非默認參數以前
    ...:     return x + y
  File "<ipython-input-15-993be842d592>", line 1
    def inc(x=1, y):
           ^
SyntaxError: non-default argument follows default argument


In [16]: def connect(host='127.0.0.1', port=3306, user='root', password='', dbname='test'):
    ...:     pass
    ...: 

In [17]: connect('192.168.110.13',password='123456')

參數默認值和關鍵字參數一塊兒使用,會讓代碼很是簡潔app

可變參數

可變參數兩種形式:ide

  • 位置可變參數 : 參數名前加一個星號, 構成元組, 傳參只能以位置參數的形式
  • 關鍵字可變參數: 參數名前加兩個信號, 構成字典, 傳參只能以關鍵字參數的形式

位置可變參數函數

In [18]: def sum(*lst):
    ...:     print(type(lst))
    ...:     ret = 0
    ...:     for x in lst:
    ...:         ret += x
    ...:     return ret
    ...: 
# 參數前加一個星號, 表示這個參數是可變的, 也就是能夠接受任意多個參數, 這些參數將構成一個元組, 此時只能經過位置參數傳參
In [19]: sum(1, 2, 3)
<class 'tuple'>
Out[19]: 6

關鍵字可變參數學習

In [20]: def connect(**kwargs):
    ...:     print(type(kwargs))
    ...:     for k, v in kwargs.items():
    ...:         print('{} => {}'.format(k, v))
    ...:         
# 參數前加兩個星號, 表示這個參數是可變的,能夠接受任意多個參數, 這些參數構成一個字典,此時只能經過關鍵字參數傳參
In [21]: connect(host='127.0.0.1',port=3306)
<class 'dict'>
host => 127.0.0.1
port => 3306

位置可變參數和關鍵字可變參數混合使用ui

In [22]: def fn(*args, **kwargs):
    ...:         print(args)
    ...:         print(kwargs)
    ...:     

In [23]: fn(1, 2, 3, a=4, b=5)
(1, 2, 3)
{'a': 4, 'b': 5}
# 以上說明位置可變參數和關鍵字可變參數能夠混合使用

In [24]: def fn(**kwargs, *args): 
  File "<ipython-input-24-e42478d184b2>", line 1
    def fn(**kwargs, *args):
                   ^
SyntaxError: invalid syntax
# 以上說明當位置可變參數和關鍵字可變參數一塊兒使用時, 位置可變參數必須在前面

可變參數和普通參數混合使用

普通參數能夠和可變參數一塊兒使用,可是傳參的時候必須匹配,演示以下

In [25]: def fn(x, y, *args, **kwargs):
    ...:         print(x)
    ...:         print(y)
    ...:         print(args)
    ...:         print(kwargs)
    ...:     

In [26]: fn(2, 3, 4, 5, 7, a=1, b=2)
2
3
(4, 5, 7)
{'a': 1, 'b': 2}

In [27]: fn(2, 3)
2
3
()
{}

In [28]: fn(2, 3, 4, 5, x=1)    # x有兩個值,一個2,一個1,因此拋出TypeError
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-0f8d856dee50> in <module>()
----> 1 fn(2, 3, 4, 5, x=1)

TypeError: fn() got multiple values for argument 'x'

In [29]: fn(2, y=3)
2
3
()
{}

位置可變參數能夠在普通參數以前, 可是在位置可變參數以後的普通參數變成了keyword-only參數:

In [30]: def fn(*args, x):
    ...:     print(args)
    ...:     print(x)
    ...:     

In [31]: fn(2, 3, 4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-31-fab2f7df0315> in <module>()
----> 1 fn(2, 3, 4)

TypeError: fn() missing 1 required keyword-only argument: 'x'

In [32]: fn(2, 3, x=4)    # 必須將位置可變參數以後的普通參數變成keyword-only,不然TypeError
(2, 3)
4

關鍵字可變參數不容許在普通參數以前,演示以下:

In [33]: def fn(**kwargs, x=5):
  File "<ipython-input-33-889f99c1c889>", line 1
    def fn(**kwargs, x=5):
                   ^
SyntaxError: invalid syntax

關於默認參數和可變參數的總結:

一般來講:

  • 默認參數靠後
  • 可變參數靠後
  • 默認參數和可變參數通常不一樣時出現
  • 當默認參數和可變參數一塊兒出現的時候, 默認參數至關於普通參數

參數解構

參數解構有兩種形式

  • 一個星號 解構的對象:可迭代對象 ,解構的結果:位置參數
  • 兩個星號 解構的對象:字典 ,解構的結果:關鍵字參數

一個星號的狀況

In [34]: def add(x, y):
    ...:         ret = x + y
    ...:         print('{} + {} = {}'.format(x, y, ret))
    ...:         return ret
    ...: 

In [35]: add(1, 2)
1 + 2 = 3
Out[35]: 3

In [36]: add(x=1, y=2)
1 + 2 = 3
Out[36]: 3

In [37]: t = [1, 2]

In [38]: add(t[0], t[1])    # 若是列表中的元素不少的時候,一個一個解開很不方便簡潔
1 + 2 = 3
Out[38]: 3

In [39]: add(*t)    # 位置參數解構  加一個星號, 能夠把可迭代對象解構成位置參數
1 + 2 = 3
Out[39]: 3

In [40]: add(*range(2))
0 + 1 = 1
Out[40]: 1

二個星號

In [42]: d = {'x': 1, 'y':2}

In [43]: add(**d)
1 + 2 = 3
Out[43]: 3

參數解構發生在函數調用時, 可變參數發生函數定義時,因此二者並不衝突

In [46]: def sum(*args):    # 可變參數發生在函數定義時
    ...:     ret = 0
    ...:     for x in args:
    ...:         ret += x
    ...:     return ret
    ...: 

In [47]: sum(*range(10))    # 參數解構發生在函數調用時
Out[47]: 45

In [48]: def fn(**kwargs):
    ...:     print(kwargs)
    ...:     

In [49]: fn(**{'a-b':1})
{'a-b': 1}

In [50]: fn(**{123:1})    # 關鍵字參數解構, key必須是str
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-50-3c8b8b3fdf0b> in <module>()
----> 1 fn(**{123:1})

TypeError: fn() keywords must be strings

keyword-only 參數

使用方法參見:Python: 函數參數列表中單個星號的意思,Keyword-Only Arguments

星號能夠以一個參數的形式出如今函數聲明中的參數列表中,但星號以後的全部參數都必須有關鍵字(keyword),這樣在函數調用時,星號*以後的全部參數都必須以keyword=value的形式調用,而不能以位置順序調用。

使用示例以下:也可參考上面連接中的示例

In [54]: def fn(*, x):
    ...:     print(x)
    ...:     

In [55]: fn(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-55-f005f2a6106f> in <module>()
----> 1 fn(3)

TypeError: fn() takes 0 positional arguments but 1 was given

In [56]: fn(x=3)
3

In [57]: def fn(x, *, y):
    ...:     print(x)
    ...:     print(y)
    ...:     

In [58]: fn(1, y=2)
1
2

In [59]: fn(1, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-59-c159019d3516> in <module>()
----> 1 fn(1, 2)

TypeError: fn() takes 1 positional argument but 2 were given

函數的返回值

  • return 語句除了返回值以外,還會結束函數, return以後的語句將不會被執行
  • 一個函數能夠有多個return語句, 執行到哪一個return由哪一個return返回結果並結束函數
  • 函數中 return能夠提早結束循環
  • 當函數沒有return語句的時候,返回None
  • 當函數須要返回多個值時, 能夠用封裝把返回值封裝成一個元組
  • 能夠經過解構獲取到多返回值
  • return None 能夠簡寫爲 return, 一般用於結束函數
In [63]: def fn(x):
    ...:     for i in range(x):
    ...:         if i > 3:
    ...:             return i    # return能夠提早退出循環
    ...:     else:
    ...:         print('not bigger than 3')
    ...:         

In [64]: fn(2)
not bigger than 3

In [65]: fn(10)    
Out[65]: 4

In [66]: def fn():
    ...:     pass    # 沒有return時返回的是None
    ...: 

In [67]: ret = fn()

In [68]: ret

In [69]: type(ret)
Out[69]: NoneType

In [70]: def fn():
    ...:     return 3, 5    # 當函數須要返回多個值時, 會把返回值封裝成一個元組
    ...: 

In [71]: ret = fn()

In [72]: type(ret)
Out[72]: tuple

In [73]: x, y = fn()    # 能夠經過解構獲取多個返回值

函數的嵌套

函數能夠嵌套使用

In [75]: def outter():
    ...:     def inner():
    ...:         print('inner')
    ...:     print('outter')
    ...:     inner()
    ...:     

In [76]: outter()
outter
inner

做用域

變量的做用域爲定義此變量的做用域

In [6]: def fn(): # 變量的做用域爲定義此變量的做用域
   ...:         xx = 1
   ...:         print(xx)
   ...:     

In [7]: fn()
1

In [8]: xx
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-8-102f5037fe64> in <module>()
----> 1 xx

NameError: name 'xx' is not defined

代表變量的做用域就在fn函數之中

上級做用域對下級做用域只讀可見

不一樣做用域變量不可見, 可是下級做用域能夠對上級做用域的變量只讀可見

In [9]: def fn():    # 上級做用域對下級做用域可見
   ...:     xx = 1
   ...:     print(xx)
   ...:     def inner():
   ...:         print(xx)
   ...:     inner()
   ...:     

In [10]: fn()
1
1

In [11]: def fn():    # 上級做用域對下級做用域只讀可見
    ...:     xx = 1
    ...:     print(xx)
    ...:     def inner():
    ...:         xx = 2
    ...:     inner()
    ...:     print(xx)
    ...:     

In [12]: fn()
1
1    # 能夠發現xx並無被下級做用域修改

不要使用全局變量global

除非你清楚的知道global會帶來什麼,而且明確的知道,非global不行, 不然不要使用global

In [13]: xx = 1

In [14]: def fn():
    ...:     global xx    # global 能夠提高變量做用域爲全局變量
    ...:     xx += 1
    ...:     

In [15]: fn()

In [16]: xx
Out[16]: 2

閉包函數

閉包定義(Wikipedia):在一些語言中,在函數中能夠(嵌套)定義另外一個函數時,若是內部的函數引用了外部的函數的變量,則可能產生閉包。閉包能夠用來在一個函數與一組「私有」變量之間建立關聯關係。在給定函數被屢次調用的過程當中,這些私有變量可以保持其持久性

通俗理解:當某個函數被當成對象返回時,夾帶了外部變量,就造成了一個閉包。

若是咱們想實現一個無限增加的計數器,能夠寫一個counter函數,函數內部進行自增就行。假定咱們按照如下寫法:就會報錯

In [17]: def counter(): 
    ...:     c = 0
    ...:     def inc():
    ...:         c += 1 # c[0] = c[0] + 1
    ...:         return c
    ...: return inc
    ...: 

In [18]: f = counter()

In [19]: f()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-19-0ec059b9bfe1> in <module>()
----> 1 f()

<ipython-input-17-9dd4cd4942f6> in inc()
      2     c = 0
      3     def inc():
----> 4         c += 1 # c[0] = c[0] + 1
      5         return c
      6     return inc

UnboundLocalError: local variable 'c' referenced before assignment

在 python 的函數內,能夠直接引用外部變量,但不能改寫外部變量,所以若是在閉包中直接改寫父函數的變量,就會發生錯誤。好比上述程序直接改寫父函數中的變量c

python的閉包中若是想改寫父函數的變量能夠用可變容器實現,這也是python2實現的惟一方式

In [1]: def counter():
   ...:     c=[0]
   ...:     def inc():
   ...:         c[0] += 1
   ...:         return c[0]
   ...:     return inc
   ...: 

In [2]: f = counter()

In [3]: f
Out[3]: <function __main__.counter.<locals>.inc>

In [4]: f()
Out[4]: 1

In [5]: f()
Out[5]: 2

In [6]: f()
Out[6]: 3

nonlocal關鍵字

在python3中改寫父變量還有一種方就是使用nonlocal關鍵字

nonlocal 關鍵字用於標記一個變量由他的上級做用域定義, 經過nonlocal標記的變量, 可讀可寫

In [7]: def counter():
   ...:     c = 0
   ...:     def inc():
   ...:         nonlocal c
   ...:         c += 1
   ...:         return c
   ...:     return inc
   ...: 

In [8]: f = counter()

In [9]: f
Out[9]: <function __main__.counter.<locals>.inc>

In [10]: f()
Out[10]: 1

In [11]: f()
Out[11]: 2

若是上級沒有定義nonlocal的變量,使用nonlocal時會拋出語法錯誤

In [12]: def fn():
    ...:     nonlocal xxx
  File "<ipython-input-12-2d2b8104e945>", line 2
    nonlocal xxx
SyntaxError: no binding for nonlocal 'xxx' found

函數的__defaults__屬性

可變參數和不可變參數的__defaults__屬性不同

參數可變時

當使用可變類型做爲默認值參數默認值時,須要特別注意,會改變函數的__default__屬性

In [1]: def fn(xxyy=[]):
   ...:     xxyy.append(1)
   ...:     print(xxyy)
   ...:     

In [2]: fn()
[1]

In [3]: fn()
[1, 1]

In [4]: fn.__defaults__        # 參數是函數對象的屬性
Out[4]: ([1, 1],)

In [5]: fn()
[1, 1, 1]

In [6]: fn.__defaults__     # 全部的函數參數封裝成一個元組,第一個函數參數時列表在動態變化
Out[6]: ([1, 1, 1],)

參數不可變時

使用不可變類型做爲默認值,函數體內不改變默認值

In [8]: def fn(x=0, y=0):
   ...:         x = 3   # 賦值即定義
   ...:         y = 3   # 賦值即定義
   ...:     

In [9]: fn.__defaults__
Out[9]: (0, 0)

In [10]: fn()

In [11]: fn.__defaults__
Out[11]: (0, 0)

可變參數時None的使用

一般若是使用一個可變類型做爲默認參數時, 會使用None來代替

In [1]: def fn(lst=None):    # 向一個列表中插入元素3,列表默認爲None
   ...:     if lst is None:
   ...:         lst = []
   ...:     lst.append(3)
   ...:     print(lst)
   ...:     

In [2]: fn.__defaults__        # 函數的__defaults__屬性就是可變參數對應的None
Out[2]: (None,)

In [3]: fn()
[3]

In [4]: fn()                # 若是不傳入值,函數執行的時候會先建立一個空列表,而後append
[3]

In [5]: fn.__defaults__
Out[5]: (None,)

In [6]: fn([1,2])
[1, 2, 3]

In [7]: fn.__defaults__        # 傳入值以後,也不會改變函數的__default__屬性
Out[7]: (None,)

Python做用域、閉包、裝飾器資料

函數執行流程

函數的執行過程就是壓棧和出棧的過程。具體以下

當調用函數的時候, 解釋器會把當前現場壓棧,而後開始執行被調函數, 被調函數執行完成,解釋器彈出當前棧頂,恢復現場

遞歸函數

遞歸函數的定義就是函數調用函數自身。

  • 遞歸函數必需要有退出條件
  • 爲了保護解釋器, Python對最大遞歸深度有限制
  • 絕大多數遞歸均可以轉化爲循環使用
  • 儘可能避免使用遞歸
  • sys模塊中的getrecursionlimit和setrecursionlimit能夠獲取和設置最大遞歸深度

匿名函數

In [1]: lambda x: x + 1
Out[1]: <function __main__.<lambda>>

匿名函數有如下特色

  • lambda來定義
  • 參數列表不須要用小括號
  • 冒號不是用來開啓新語句塊
  • 沒有return,最後一個表達式的值即返回值
  • 匿名函數(lambda表達式)只能寫在一行上,因此也叫單行函數

匿名函數的好處是

  • 函數沒有名字,沒必要擔憂函數名衝突
  • 匿名函數也是一個函數對象,能夠把匿名函數返回給一個變量,再利用變量調用函數
In [1]: lambda x: x + 1
Out[1]: <function __main__.<lambda>>

In [2]: f = lambda x: x + 1        # 直接把lambda函數返回給變量f

In [3]: f(3)                    # 由變量f調用函數
Out[3]: 4

In [4]: f(5)
Out[4]: 6

In [5]: (lambda x: x * 2)(3)    # 第一對括號用來改變優先級 第二對括號表示函數調用
Out[5]: 6

In [6]: (lambda : 1)()            # lambda表示式參數能夠爲空
Out[6]: 1

In [7]: (lambda x, y: x + y)(3, 5)    # lambda表達式的位置參數
Out[7]: 8

In [8]: (lambda *args: args)(*range(3))    # lambda表達式的位置可變參數
Out[8]: (0, 1, 2)

In [9]: (lambda *args, **kwargs: print(args, kwargs))(*range(3), **{str(x):x for x in range(3)})    # lambda表達式的位置可變參數和關鍵字可變參數
(0, 1, 2) {'0': 0, '1': 1, '2': 2}

In [10]: (lambda *, x: x)(x=3)    # *號後面的位置參數必須使用關鍵字參數
Out[10]: 3
普通函數所支持的參數的變化,匿名函數都支持

匿名函數的常見用法:一般用於高階函數的參數, 當此函數很是短小的時候,就適合使用匿名函數

好比匿名函數能夠做爲sorted函數的自定義鍵函數(custom key function)

In [11]: help(sorted)
    Help on built-in function sorted in module builtins:

    sorted(iterable, key=None, reverse=False)
        Return a new list containing all items from the iterable in ascending order.

        A custom key function can be supplied to customise the sort order, and the
        reverse flag can be set to request the result in descending order.

In [12]: from collections import namedtuple

In [13]: point = namedtuple('point',['x','y'])    # 定義命名元組point

In [14]: points = [point(1, 2), point(4, 3), point(8, 9)]

In [15]: def getY(point):
    ...:     return point.y
    ...: 

In [16]: sorted(points, key=getY)    # 簡短的函數能夠做爲自定義鍵函數
Out[16]: [point(x=1, y=2), point(x=4, y=3), point(x=8, y=9)]

In [17]: sorted(points, key=lambda x: x.y)    # lambda表示也能夠做爲自定義鍵函數
Out[17]: [point(x=1, y=2), point(x=4, y=3), point(x=8, y=9)]

高階函數

高階函數的定義

高階函數英文叫Higher-order function。

在數學和計算機科學中,高階函數是至少知足下列一個條件的函數:

  • 接受一個或多個函數做爲輸入:一般用於大多數邏輯固定,少部分邏輯不固定的場景
  • 輸出一個函數:函數做爲返回值: 一般是用於閉包的場景, 須要封裝一些變量

常見的高階函數有map,reduce,filter

高階函數:插入排序

插入排序時,排序順序分爲升序和降序,咱們可使用一個函數做爲插入排序函數的參數來控制是升序仍是降序。

首先看一下按照升序插入排序,而後再改進成升序降序可控的插入排序

def insertSort(iter):
    ret = []
    for x in iter:
        for i, y in enumerate(ret):
            if x < y:                # 修改處
                ret.insert(i, x)
                break
        else:
            ret.append(x)
    return ret

若是想讓這個函數降序排序,則只須要修改代碼中的註釋處,改爲x > y便可

若是傳入一個函數來控制if後面的bool值,則就實現了經過參數控制升降了

def insertSort(iter, cmp = lambda x, y: x < y):
    ret = []
    for x in iter:
        for i, y in enumerate(ret):
            if cmp(x, y):
                ret.insert(i, x)
                break
        else:
            ret.append(x)
    return ret

這個函數就默認爲升序排序了,可是能夠傳入一個比較函數變成降序,以下

lst = insertSort([1, 3, 2, 4, 6, 8, 5],lambda x, y: x > y)

map

map()函數原型:map(func, *iterables) --> map object

map()函數接收兩個參數,一個是函數func,一個是可迭代對象Iterablemap將傳入的函數依次做用到可迭代對象的每一個元素,並把結果放入map對象這個迭代器中。因此map函數是高階函數。

map類中存在__iter____next__函數

map使用示例

把list中的全部數字的平方

In [1]: def f(x):                                # 定義平方函數f
   ...:         return x ** 2
   ...: 

In [2]: ret = map(f, [1, 2, 3, 4, 5, 6, 7])        # 函數f和列表做爲map的參數

In [3]: ret                                        # map的返回值只是一個返回值
Out[3]: <map at 0x7f2d539a7470>

In [4]: next(ret)                                # 能夠用next方法輸出map的結果
Out[4]: 1

In [5]: next(ret)
Out[5]: 4

In [6]: lst = list(ret)                            # 也能夠用list函數計算出全部的值

In [7]: lst
Out[7]: [9, 16, 25, 36, 49]

reduce

map函數是map類的函數,可是reduce函數屬於functools包的reduce模塊中

from functools import reduce

而後可使用help方法查看reduce函數的使用

help(reduce)

輸出結果以下

Help on built-in function reduce in module _functools:

reduce(...)
    reduce(function, sequence[, initial]) -> value

    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

reduce使用示例

  • 輸出1到10的和
def add(x,y): 
    return x + y
print(reduce(add, range(1, 11)))

輸出結果爲55

  • 把字符串轉化爲int,不適用int()函數
def str2int(s):
    def char2num(c):
        return {'0': 0, '1': 1, '2': 2 ,'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[c]
    def f(x, y):
        return 10 * x + y 
    return reduce(f, map(char2num, s))
str2int('1234321') => 1234321

filter

help(filter)以後能夠發現filter是一個類,其中有一個filter函數,原型以下

filter(function or None, iterable) --> filter object

map()相似,filter()也接收一個函數和一個序列。和map()不一樣的是,filter()把傳入的函數依次做用於每一個元素,而後根據返回值是True仍是False決定保留仍是丟棄該元素。返回值也是一個迭代器。

filter使用示例

使用filter篩選出list中的迴文數

def is_palindrome(n):
    m = str(n)
    for i in range(len(m)//2):
        if m[i] != m[len(m) - i -1]:
            return False
    else:
        return True

lst = list(filter(is_palindrome, [12321, 194, 13431]))
print(lst)
# 結果: [12321, 13431]

因此filter()函數用於過濾序列,重點在於選擇一個正確的篩選函數。

生成器

帶yield語句的函數稱之爲生成器函數, 生成器函數的返回值是生成器

  • 生成器函數執行的時候,不會執行函數體
  • 當next生成器的時候, 當前代碼執行到以後的第一個yield,會彈出值,而且暫停函數
  • 當再次next生成器的時候,從上次暫停處開始往下執行
  • 當沒有多餘的yield的時候,會拋出StopIteration異常,異常的value是函數的返回值

生成器的基本形式

In [1]: def g():
    ...:     for x in range(5):
    ...:         yield x    # 彈出x
    ...:         

In [2]: r = g()        # 函數調用完成以後函數現場並無被銷燬

In [3]: r
Out[3]: <generator object g at 0x7f0e18543990>

In [4]: next(r)
Out[4]: 0

In [5]: next(r)
Out[5]: 1

In [6]: for x in r:
    ...:     print(x)
    ...:     
2
3
4

生成器的執行順序

In [1]: def g():
   ...:     print('a')
   ...:     yield 1
   ...:     print('b')
   ...:     yield 2
   ...:     return 3
   ...: 

In [2]: r = g()    # 執行生成器函數的時候函數並無被執行 

In [3]: next(r)    # 執行到第一個yield就中止執行
a
Out[3]: 1

In [4]: next(r)    # 執行到第二個yield就中止執行
b
Out[4]: 2

In [5]: next(r)    # 從第二個yield開始,當沒有更多yield的時候,拋出StopIteration異常,異常的值正好是return的返回值
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-5-0b5056469c9c> in <module>()
----> 1 next(r)

StopIteration: 3

生成器的應用

計數器第一種形式

In [1]: def counter():
   ...:     x = 0
   ...:     while True:
   ...:         x += 1
   ...:         yield x    # 每次將+1以後的x彈出
   ...:         

In [2]: def inc(c):
   ...:     return next(c)
   ...: 

In [3]: c = counter()    # counter函數執行的結果就是一個生成器,因此c就是生成器

In [4]: inc(c)
Out[4]: 1

In [5]: inc(c)
Out[5]: 2

計數器第二種形式

In [6]: def make_inc():
   ...:     def counter():
   ...:         x = 0
   ...:         while True:
   ...:             x += 1
   ...:             yield x
   ...:     c = counter()
   ...:     return lambda : next(c)    # 使用lambda表達式將next(c)做爲函數返回,而不是隻返回一個next(c)
   ...: 

In [7]: make_inc()
Out[7]: <function __main__.make_inc.<locals>.<lambda>>    # make_inc本質是一個匿名函數

In [8]: inc = make_inc()

In [9]: inc()
Out[9]: 1

In [10]: inc()
Out[10]: 2

斐波拉契數列

In [11]: def fib():
    ...:     a = 1
    ...:     b = 1
    ...:     while True:
    ...:         yield a
    ...:         a, b = b, a + b
    ...:         

In [12]: fib()
Out[12]: <generator object fib at 0x7f9ff2746830>

In [13]: f = fib()    # 生成器f

In [15]: next(f)
Out[15]: 1

In [16]: next(f)
Out[16]: 1

In [17]: next(f)
Out[17]: 2

In [18]: next(f)
Out[18]: 3

In [19]: g = fib()

In [20]: ret = []    # 將yield的值都保存在ret中

In [21]: for _ in range(1000):    # 遍歷生成器
    ...:     ret.append(next(g))
    ...:     

In [22]: ret[-1]    # 取ret列表的最後一個元素值,速度很快
Out[22]: 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875

生成器的高級用法

生成器的高級用法是協程

  • 協程:協程運行在一個線程以內, 在用戶態調度

生成器參考資料


記得幫我點贊哦!

精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你須要的學習資料,還在等什麼?快去關注下載吧!!!

resource-introduce

念念不忘,必有迴響,小夥伴們幫我點個贊吧,很是感謝。

我是職場亮哥,YY高級軟件工程師、四年工做經驗,拒絕鹹魚爭當龍頭的斜槓程序員。

聽我說,進步多,程序人生一把梭

若是有幸能幫到你,請幫我點個【贊】,給個關注,若是能順帶評論給個鼓勵,將不勝感激。

職場亮哥文章列表:更多文章

wechat-platform-guide-attention

本人全部文章、回答都與版權保護平臺有合做,著做權歸職場亮哥全部,未經受權,轉載必究!

相關文章
相關標籤/搜索