2010-03-29 59 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