1. 程式人生 > >Python的yield用法與原理

Python的yield用法與原理

翻了一篇workflow上關於yield的用法,翻的有點爛,在這裡貽笑大方了,慢慢來,總是期待著一點一點的進步。

為了理解yield的機制,我們需要理解什麼是生成器。在此之前先介紹迭代器iterables。

Iterables

當你建立一個list,你可以一個一個的獲取,這種列表就稱為迭代:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

Mylist 是一個迭代器. 當你理解它為一個list,它便是可迭代的:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

任何可以用 for in 來迭代讀取的都是迭代容器,例如lists,strings,files.這些迭代器非常的便利,因為你可以想取多少便取多少,但是你得儲存所有的值,其中很多值都完全沒有必要每次都保持在記憶體中。

Generators

Generators(生成器)也是可迭代的,但是你每次只能迭代它們一次,因為不是所有的迭代器都被一直儲存在記憶體中的,他們臨時產生這些值:

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

生成器幾乎和迭代器是相同的,除了符號[]變為()。但是你無法用兩次,因為他們只生成一次:他們生成0然後丟棄,繼續統計1,接著是4,一個接著一個。

Yield

Yield的用法有點像return,除了它返回的是一個生成器,例如:

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

上面的例子幾乎非常積累,但是它很好的闡釋了yield的用法,我們可以知道createGenerator()生成的是一個生成器。

為了掌握yield的精髓,你一定要理解它的要點:當你呼叫這個函式的時候,你寫在這個函式中的程式碼並沒有真正的執行。這個函式僅僅只是返回一個生成器物件。有點過於奇技淫巧:-)

然後,你的程式碼會在每次for使用生成器的時候run起來。

現在是解釋最難的地方:

當你的for第一次呼叫函式的時候,它生成一個生成器,並且在你的函式中執行該迴圈,知道它生成第一個值。然後每次呼叫都會執行迴圈並且返回下一個值,知道沒有值返回為止。該生成器背認為是空的一旦該函式執行但是不再刀刀yield。之所以如此是因為該迴圈已經到達終點,或者是因為你再也不滿足“if/else”的條件。

Your code explained

例子: 生成器:
# 這裡你建立一個node物件的一個生成器生成方法Here you create the method of the node object that will return the generator
def node._get_child_candidates(self, distance, min_dist, max_dist):

  # 這裡是每次被呼叫的程式碼Here is the code that will be called each time you use the generator object:
    
  # 如果還有一個左孩子節點If there is still a child of the node object on its left
  # 並且距離可以,返回下一個孩子節點AND if distance is ok, return the next child
  if self._leftchild and distance - max_dist < self._median:
      yield self._leftchild

  # 如果還有一個右孩子幾點If there is still a child of the node object on its right
  # 並且距離可以,返回下一個孩子節點AND if distance is ok, return the next child
  if self._rightchild and distance + max_dist >= self._median:
      yield self._rightchild

  # 如果方法執行到這裡,生成器會被認為為空If the function arrives here, the generator will be considered empty
  # there is no more than two values: the left and the right children

呼叫者:

# 建立一個空的列表Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# 迴圈candidates列表,只有一個元素。Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidates list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

這段程式碼包含一些非常機智的部分:

    1. list的迴圈迭代部分,但是list在迴圈的同時又在拓展,:)這種方法是一種迴圈內嵌式的資料的相對簡潔的方法,但是又存在著一些風險可能會導致死迴圈的情況。在這個例子當中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 耗盡所有的的生成器的值,但是當保持生成新的生成器物件,並且依據之前生成器產生許多不同的值,由於它產生於不同的節點。

    2. extend()方法是一個list 物件方法,它產生一個迭代器並且新增它的值到list當中去。

通常我們

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但是程式碼中獲得一個生成器,這種方式比較好的原因如下:

首先是你無須讀取該值兩次。

然後你不需要把所有的值都放在記憶體中。

與此同時,它能夠owrk的原因是Python不關心一個方法的引數石佛是一個list.期待是一個迭代器所以它能夠適用於strings,lists,tuples以及生成器。這被稱為動態型別或者鴨子型別(duck typing)是python 如此酷的一大原因。鴨子型別又是另外一個問題了,blablabla。

現在讓我們來看看一些高階的用法:

控制生成器資源消耗:

>>> class Bank(): # let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

這一個非常的有用,特別是類似的資源訪問控制。

Itertools模組

Itertools模組包含一些特別的函式去執行迭代器。有沒有想過去複製一個生成器 或者連結兩個生成器?等等。

引入itertools就好了,import itertools.

下面舉個例子.看看四匹馬到達先後順序的例子:

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

最後是理解迭代器的內部機制:

Iteration is a process implying iterables (implementing the __iter__() method) and iterators (implementing the __next__() method). Iterables are any objects you can get an iterator from. Iterators are objects that let you iterate on iterables.

更多的相關內容可以閱讀 迴圈如何工作.

相關推薦

List詳細用法原理解析

List是Collection介面的子介面,他可以定義一個允許重複的線性集合,即允許存放相同的元素,但是存放的位置不同。與collection不同的是,List新增了列表迭代器ListInterator。 與Collection介面相比,List介面新增瞭如下方法: a

Spark SQL入門用法原理分析

sparkSQL是為了讓開發人員擺脫自己編寫RDD等原生Spark程式碼而產生的,開發人員只需要寫一句SQL語句或者呼叫API,就能生成(翻譯成)對應的SparkJob程式碼並去執行,開發變得更簡潔 注意:本文全部基於SparkSQL1.6 一. API Spark SQ

Python的yield用法原理

翻了一篇workflow上關於yield的用法,翻的有點爛,在這裡貽笑大方了,慢慢來,總是期待著一點一點的進步。 為了理解yield的機制,我們需要理解什麼是生成器。在此之前先介紹迭代器iterables。 Iterables 當你建立一個list,你可以一個一個

LruCache原理用法LinkedHashMap

一.LruCache演算法 LruCache演算法就是Least Recently Used,也就是最近最少使用演算法。 他的演算法就是當快取空間滿了的時候,將最近最少使用的資料從快取空間中刪除以增加可用的快取空間來快取新內容。 這個算分的內部有一個快取列

斷點 相關技術原理(2)

def pan 保存 ollydbg php class 工具 code http 繼續對OD的斷點技術做個筆記。 1、硬件斷點: Intel CPU中有8個調試寄存器(Debug Register)DR0 — DR7,當中DR0 — DR3用於設置硬件斷點地址,D

淺談mmap()和ioremap()的用法區別

12只 設備 gpa 我們 之間 mmap ioremap shared set 一、mmap()mmap()函數是用來將設備內存線性地址映射到用戶地址空間。(1)首先映射基地址,再通過偏移地址尋址;(2)unsigned char *map_cru_base=(unsig

nagios簡介原理

nagios簡介與原理1.Nagios簡介1.與cacti的區別a) Cacti1.Cacti比較著重於直觀數據的監控,易於生成圖形,用來監控網絡流量、cpu使用率、硬盤使用率等可以說很在合適不過2.通過SNMP監控數據3.展示工具4.用插件來增加模塊做監控b) nagios 1.比較註重於主機和服務的監控,

關於數組方法中delete()splice()的用法不同點

索引 true 元素 關於 class str 刪除數組元素 ges fin 關於數組方法中delete()與splice()的用法與不同點 一.delete arr[i] 刪除數組的指定索引的項,刪除的時候該項還是會占據原來數組的位置,只是該位置的值變成了undefin

jvm特性原理---------->jvm運行時數據區分區

程序計數器 如果 wid 實例 over 技術分享 xmx ins 調用 1.概述: 內存分區:JVM會把自己所管理的所有內存區域進行分區。 各個區域的服務對象 各個區域中分別存放了什麽內容 存放的數據是如何創建的 這些數據在各個區域中存放,存儲的布局是什麽

php運行機制原理

數據結構 機制 二進制 純c 轉換 請求 程序 輸出 ont php運行機制與原理: PHP總共有三個模塊:內核、Zend引擎、以及擴展層; 1. PHP內核用來處理請求、文件流、錯誤處理等相關操作; 2. Zend引擎(ZE)用以將源文件轉換成機器語言(二進制),然後

_IO, _IOR, _IOW, _IOWR 宏的用法解析

內存 返回值 bits 分辨 上傳 正是 ron 了解 asm 今天在寫字符驅動驗證程序的時候要用到ioctl函數,其中有一個cmd參數,搞了半天也不了解是什麽意思,那個cmd還有什麽命令碼了什麽的,還好google下,覺得這篇文章寫的不錯,就轉來看看:在驅動程序裏, io

js中slice、splice用法區別

delet 內容 title pan ont 指定 至少 一個 拷貝 1.slice(start,end)(參數可選) slice() 方法返回一個從開始到結束(不包括結束)選擇的數組的一部分淺拷貝到一個新數組對象。原始數組不會被修改。 var a = [‘a‘, ‘b‘

【數據壓縮】JPEG標準原理解析

round 高頻 切割 基於 大小 image 生成 p s pan 轉載請註明出處:http://blog.csdn.net/luoshixian099/article/details/50392230 CSDN-勿在浮沙築高臺 為了滿足不同應用的需求,J

letexpr命令的用法實戰案例

let expr 命令 let命令的用法格式:let 賦值表達式【註】let賦值表達式功能等同於:(賦值表達式)例子:給自變量i加8[[email protected]/* */ ~]# i=2 [[email protected]/* */ ~]# let i=i+8 [

fullcalendar的用法小結

hang web .cn www https http tag 用法 cal https://www.helloweba.com/tag-fullcalendar.html fullCalender的用例 http://www.cnblogs.com/zhangwei595

oracle 之 偽列 rownum 和 rowid的用法區別

lena select 區別 name 繼續 class 重復 clas 重復數據 rownum的用法 select rownum,empno,ename,job from emp where rownum<6 可以得到小於6的值數據 select rownum,e

ElasticSearch的基本用法集群搭建

sage zip alt mob new t catch div 數據類型 聚合 一、簡介 ElasticSearch和Solr都是基於Lucene的搜索引擎,不過ElasticSearch天生支持分布式,而Solr是4.0版本後的SolrCloud才是分布式版本,Sol

Spring Boot實戰原理分析視頻課程

spring boot 視頻課程 實戰與原理分析 1、Spring Boot概述與課程概要介紹2、Spring4 快速入門3、Spring4 擴展分析(一)4、Spring4 擴展分析(二)5、Spring Boot 快速入門6、Spring Boot 配置分析(一)7、Spring Boot 配

J2EE--Servlet生命周期原理

eric erl rect 不同 必須 小程序 表單 exce strong Servlet是在server上執行的小程序.而在java中應用程序多是在容器中進行生命周期的管理(這裏指Tomact容器). Servlet主要的架構圖

SQL中MINUS的用法UNION的用法

保留 所有 一個 出現 但是 列數 sql date store 一:MINUS指令 其是運用在兩個 SQL 語句上。它先找出第一個 SQL 語句所產生的結果,然後看這些結果有沒有在第二個 SQL語句的結果中。如果有的話,那第一個SQL結果數據就被去除,而不會在最後的結果中