主頁 > 後端開發 > Python函式詳解

Python函式詳解

2020-09-28 18:18:42 後端開發

函式是Python里組織代碼的最小單元,Python函式包含以下幾個部分:

  • 定義函式
  • 呼叫函式
  • 引數
  • 函式的回傳值
  • 函式的嵌套
  • 作用域
  • 函式執行流程
  • 遞回函式
  • 匿名函式
  • 生成器
  • 高階函式

定義函式

def add(x, y):     # 函式定義 def 表示定義一個函式, 緊接著是函式名 函式名后面用一對小括號列出引數串列,引數串列后面使用一個冒號開始函式體
    print(x + y)   # 函式體是正常的Python陳述句,可以包含任意結構
    return  x + y  # return 陳述句表示函式的回傳值

函式是有輸入(引數)和輸出(回傳值)的代碼單元, 把輸入轉化為輸出

呼叫函式

定義函式的時候,并不會執行函式體, 當呼叫函式的時候,才會執行其中的陳述句塊

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')

引數默認值和關鍵字引數一起使用,會讓代碼非常簡潔

可變引數

可變引數兩種形式:

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

位置可變引數

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

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

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 的閉包和裝飾器

  • 說說Python中的閉包 - Closure

  • Python Enclosing作用域、閉包、裝飾器話聊上篇

  • Python Enclosing作用域、閉包、裝飾器話聊下篇

函式執行流程

函式的執行程序就是壓堆疊和出堆疊的程序,具體如下

當呼叫函式的時候, 解釋器會把當前現場壓堆疊,然后開始執行被調函式, 被調函式執行完成,解釋器彈出當前堆疊頂,恢復現場

遞回函式

遞回函式的定義就是函式呼叫函式自身,

  • 遞回函式必須要有退出條件
  • 為了保護解釋器, 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

生成器的高級用法

生成器的高級用法是協程

  • 協程:協程運行在一個執行緒之內, 在用戶態調度

生成器參考資料

  • python生成器到底有什么優點?

  • Understanding Generators in Python

  • Introduction to Python Generators

  • 生成器資料匯總


記得幫我點贊哦!

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

resource-introduce

念念不忘,必有回響,小伙伴們幫我點個贊吧,非常感謝,

我是職場亮哥,YY高級軟體工程師、四年作業經驗,拒絕咸魚爭當龍頭的斜杠程式員,

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

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

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

wechat-platform-guide-attention

本人所有文章、回答都與著作權保護平臺有合作,著作權歸職場亮哥所有,未經授權,轉載必究!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/134891.html

標籤:其他

上一篇:Python爬蟲實戰:制作各大音樂平臺的聚合的音樂下載器

下一篇:編程體系結構(04):JavaIO流檔案管理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more