2010-03-29 106 views
5

我已經研究過發電機功能,我想我明白了,但是我想了解哪裏可以在我的代碼中應用它。你在哪裏使用Python代碼中的生成器功能?

我想到下面的例子中我在「巨蟒重要的參考」一書閱讀:

# tail -f 
def tail(f): 
    f.seek(0,2) 
    while True: 
    line = f.readline() 
    if not line: 
    time.sleep(0.1) 
    continue 
    yield line 

你有其中發電機是一樣的尾巴-f工作的最佳工具的任何其他有效的例子嗎?

您經常使用生成器功能以及您通常使用哪種類型的功能\部分程序?

+1

請將此設爲社區wiki問題。 – 2010-03-29 10:21:53

回答

6

當我實現掃描程序(tokenizer)或迭代數據容器時,我使用它們很多。

編輯:這裏是一個演示標記生成器我用於C++語法突出顯示程序:

whitespace = ' \t\r\n' 
operators = '~!%^&*()-+=[]{};:\'"/?.,<>\\|' 

def scan(s): 
    "returns a token and a state/token id" 
    words = {0:'', 1:'', 2:''} # normal, operator, whitespace 
    state = 2 # I pick ws as first state 
    for c in s: 
     if c in operators: 
      if state != 1: 
       yield (words[state], state) 
       words[state] = '' 
      state = 1 
      words[state] += c 
     elif c in whitespace: 
      if state != 2: 
       yield (words[state], state) 
       words[state] = '' 
      state = 2 
      words[state] += c 
     else: 
      if state != 0: 
       yield (words[state], state) 
       words[state] = '' 
      state = 0 
      words[state] += c 
    yield (words[state], state) 

用例:

>>> it = scan('foo(); i++') 
>>> it.next() 
('', 2) 
>>> it.next() 
('foo', 0) 
>>> it.next() 
('();', 1) 
>>> it.next() 
(' ', 2) 
>>> it.next() 
('i', 0) 
>>> it.next() 
('++', 1) 
>>> 
+0

你能發佈一些簡單的標記器片段嗎? – systempuntoout 2010-03-29 08:12:21

+0

@systempuntoout,好的,我發佈了一個示例。 – 2010-03-29 08:39:48

+0

很好的例子,非常感謝! – systempuntoout 2010-03-29 09:00:12

4

每當你的代碼,要麼產生值的無限數量或者更一般地,如果太多內存將通過第一生成整個列表被消耗掉。

,或者如果可能,你遍歷整個生成的列表(和列表是非常大)。我的意思是,如果不使用它,先生成每個值(並等待世代)是毫無意義的。

我最近遇到的問題是當我實現一個線性遞歸序列(LRS)時,斐波那契數列。

+1

-1:聽起來對我來說更像是對一般迭代器的描述,而不是生成器函數,所以它忽略了這一點。爲什麼這個答案得到任何upvotes? – nikow 2010-03-29 10:53:50

+0

@nikow:是的,它更一般,但我不會說它是迭代器的*描述*。這是關於在哪些情況下生成器可能有用的抽象描述。生成器是某種迭代器.. :) – 2010-03-29 11:24:39

1

一般來說,單獨的數據AQUISITION(這可能是複雜的)來自消費。特別是:

  • ,從而連接多個b樹查詢的結果 - 分貝部分生成並執行查詢yield從每一個-ing記錄,消費者只能看到一個到達的數據項。
  • 緩衝(預讀) - 發生器以塊爲單位獲取數據並從每個塊中產生單個元素。再次,消費者與血統細節分開。

發電機也可以作爲協同工作。您可以將數據轉換爲,在'消費者'一方使用nextval=g.next(data),發電機一方使用data = yield(nextval)。在這種情況下,發電機和消費者的「交換」值。您甚至可以在生成器上下文中使yield發生異常:g.throw(exc)可以做到這一點。

+0

緩衝是一個很好的例子,謝謝。 – systempuntoout 2010-03-29 10:14:39

2

在我有算法讀取任何東西的所有情況下,我只使用生成器。

爲什麼?

在多個生成器的上下文中,分層過濾,映射和縮減規則非常容易。

例子:

def discard_blank(source): 
    for line in source: 
     if len(line) == 0: 
      continue 
     yield line 

def clean_end(source): 
    for line in source: 
     yield line.rstrip() 

def split_fields(source): 
    for line in source; 
     yield line.split() 

def convert_pos(tuple_source, position): 
    for line in tuple_source: 
     yield line[:position]+int(line[position])+line[position+1:] 

with open('somefile','r') as source: 
    data= convert_pos(split_fields(discard_blank(clean_end(source))), 0) 
    total= 0 
    for l in data: 
     print l 
     total += l[0] 
    print total 

我的選擇是使用許多小型發電機,這樣一個小的變化是不是破壞性的整個過程鏈。

+0

功能不能正常工作嗎? – 2010-03-29 10:27:27

+2

所以你只是使用生成器函數作爲迭代器裝飾器的方便表示法。我認爲Nick D的例子要好得多,因爲它突出了延續性方面。 – nikow 2010-03-29 10:56:32

+0

@J。 T. Hurley:我不知道「同樣如此」是什麼意思,但是發電機不會產生中間結果,而功能通常是這樣。嵌套的生成器是一種map-reduce管道。 – 2010-03-29 14:48:46