python函數式編程介紹之 yield表達式形式詳解

  前言

  yield的英文單詞意思是生產,剛接觸Python的時候感到非常困惑,一直沒弄明白yield的用法。最近又重新學習了下,所以整理了下面這篇文章,供自己和大家學習參考,下面話不多說了,來一起看看詳細的介紹吧。

  先來看一個例子

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  next(g)

  在上面的例子裏,因爲foo函數中有yield關鍵字,所以foo()函數的執行結果g是一個生成器,此時可以使用next(g)或者g.next()方法觸發生成器的執行

  程序的執行結果爲

  starting...

  使用next(g)觸發生成器的執行時,程序會按照正常的順序從上向下執行,遇到yield,程序就會暫停

  並把yield後面所接的值返回

  打印next(g)的執行結果

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  print(next(g))

  程序執行結果

  starting...

  None

  在上面的例子裏,執行一次next(g)方法,程序暫停在yield那一行,此時再次調用next(g),程序會從yield語句那一行繼續向下運行

  修改上面的代碼,多調用幾次next方法,並打印next方法的返回結果

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  print(next(g))

  print("*"*20)

  print(next(g))

  上面這段代碼的執行結果爲

  starting...

  None

  ********************

  res: None

  None

  可以看到,程序確實按猜想的步驟運行,但是上面的程序也有一個很明顯的缺點:那就是上面的代碼沒有任何的實際意義:res的值永遠爲None

  在實際的開發中,使用yield表達式形式的目的是yield可以得到一個值,然後yield把這個值賦值給某個變量,這樣纔有實際意義

  那應該怎麼操作才能爲res變量賦一個值呢??那就是調用生成器自身的send方法

  send方法可以觸發一次生成器執行,同時還可以把send方法的參數傳遞給yield

  修改上面的代碼

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  next(g)

  print(g.send(5))

  程序的執行結果爲:

  starting...

  res: 5

  None

  來分析一下上面的代碼的執行過程 :

  1.程序開始執行以後,因爲foo函數中有yield關鍵字,所以foo函數並不會真的執行,而是先得到一個生成器g.

  2.直到調用next方法,foo函數正式開始執行,先執行foo函數中的print方法,然後進入while循環

  3.程序遇到yield關鍵字,程序暫停,此時next(g)語句執行完成

  4.程序執行g.send(5),程序會從yield關鍵字那一行繼續向下運行,send會把5這個值傳遞給yield

  5.yield接收到send方法傳遞過來的值,然後由yield賦值給res變量

  6.由於send方法中包含next()方法,所以程序會繼續向下運行執行print方法,然後再次進入while循環

  7.程序執行再次遇到yield關鍵字,yield會返回後面的值,由於yield後面沒有接任何參數,所以yield會返回None,程序再次暫停,直到再次調用next方法或send方法

  修改代碼,多次調用send方法

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  next(g)

  print(g.send(5))

  print("*"*20)

  print(g.send(10))

  print("#"*20)

  print(g.send(15))

  執行程序,得到如下結果

  starting...

  res: 5

  None

  ********************

  res: 10

  None

  ####################

  res: 15

  None

  可以看到,上面代碼的執行過程如同上面的分析的執行過程一樣運行

  在上面的例子裏,如果調用send方法時,傳遞的參數爲None,得到的結果會是怎麼樣的呢??

  從上面的分析中,可以知道:

  如果g.send()方法發送給yield關鍵字的參數爲None,則yield關鍵字傳遞給res變量的值就爲None

  由於yield後面本來沒有接任何值,所以yield返回的值默認也爲None,所以程序執行結果會得到兩個None

  修改代碼,驗證上面的猜想

  def foo():

  print("starting...")

  while True:

  res = yield

  print("res:",res)

  g = foo()

  next(g)

  print("#"*20)

  print(g.send(None))

  查看程序的執行結果

  starting...

  ####################

  res: None

  None鄭州婦科醫院哪家好 yiyuan.120ask.com/zzfck/

  從程序的執行結果可以看出,如果調用生成器的send方法時,傳遞的參數爲None,則程序執行的結果將會是兩個None

  使用yield表達式形式實現linux系統中的"grep -rl root /etc"命令

  代碼如下:

  import os

  def init(func):

  def wrapper(*args, **kwargs):

  g = func(*args, **kwargs)

  next(g)

  return g

  return wrapper

  @init

  def get_file_path(target):

  """

  get file abspath

  # 階段一:遞歸找文件的絕對路徑,把文件的完事路徑發送給階段二

  :param target:

  :return:

  """

  while True:

  start_path = yield

  g = os.walk(start_path)

  for parent_dir, _, files in g:

  for file in files:

  file_path = r"%s\%s" % (parent_dir, file)

  target.send(file_path)

  @init

  def opener(target):

  """

  get file obj

  # 階段二:收到文件的完整路徑,打開文件獲取文件對象,把文件對象發送給階段三

  :param target:

  :return:

  """

  while True:

  file_path = yield

  with open(file_path, encoding='utf-8') as f:

  target.send((file_path, f))

  @init

  def cat_file(target):

  """

  read file content

  # 階段三:收到文件對象,for循環讀取文件的每一行內容,把每一行內容發給階段四

  :param target:

  :return:

  """

  while True:

  file_path, f = yield

  for line in f:

  file_content = target.send((file_path, line))

  if file_content:

  break

  @init

  def grep(target, pattern):

  """

  grep function

  # 階段四:收到文件的一行內容,判斷要查找的內容是否在這一行中,如果在,則把文件名發送給階段五

  :param target:

  :param pattern:

  :return:

  """

  tag = False

  while True:

  file_path, line = yield tag

  tag = False

  if pattern in line:

  target.send(file_path)

  tag = True

  @init

  def printer():

  """

  print file name

  # 階段五:收到文件名,打印結果

  :return:

  """

  while True:

  filename = yield

  print(filename)

  path1 = "/root" # 定義要搜索的路徑

  path2 = "/etc" # 定義要搜索的路徑

  g = get_file_path(opener(cat_file(grep(printer(), "root"))))

  print(g)

  g.send(path1)

  g.send(path2)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章