2010-03-29 8 views
5

He estudiado la función de generadores y creo que la obtuve pero me gustaría saber dónde podría aplicarla en mi código.¿Dónde utilizas la función de generadores en tu código python?

que tengo en mente el siguiente ejemplo que he leído en "Python referencia esencial" del libro:

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

¿Tiene usted algún otro ejemplo eficaz donde los generadores son la mejor herramienta para el trabajo como tail-f?

¿Con qué frecuencia utiliza la función de generadores y en qué tipo de funcionalidad \ parte del programa lo aplica habitualmente?

+1

Por favor haga de esta una pregunta wiki de la comunidad. –

Respuesta

6

Los uso mucho cuando implemento escáneres (tokenizadores) o cuando itero sobre contenedores de datos.

Editar: aquí hay una demostración tokenizer utilicé para un programa resaltado de sintaxis C++: ejemplo

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) 

Uso:

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

¿Podría publicar algún fragmento simple de tokenizadores? – systempuntoout

+0

@systempuntoout, ok, publiqué una muestra. –

+0

¡Buen ejemplo, muchas gracias! – systempuntoout

4

Cada vez que su código sería o bien generar un número ilimitado de valores o, más generalmente, si la memoria demasiado sería consumido mediante la generación de toda la lista al principio.

O si es probable que usted no lo hace iterar sobre toda la lista generada (y la lista es muy grande). Quiero decir que no tiene sentido generar cada valor primero (y esperar la generación) si no se usa.

Mi último encuentro con los generadores fue cuando implementé una secuencia recurrente lineal (LRS), como p. la secuencia de Fibonacci.

+1

-1: Me parece más a una descripción de los iteradores en general, no a las funciones del generador, por lo que se pierde el sentido. ¿Por qué esta respuesta obtuvo votos ascendentes? – nikow

+0

@nikow: Sí, es más general, pero no diría que es una * descripción * de iteradores. Es una descripción abstracta sobre en qué situaciones los generadores podrían ser útiles. Los generadores son un tipo de iteradores ... :) –

1

En general, para adquisición de datos separada (que podría ser complicado) del consumo En particular:

  • para concatenar los resultados de varias consultas de árbol B - la parte db genera y ejecuta las consultas yield -ing registros de cada uno, el consumidor sólo ve elementos de datos individuales que llegan.
  • almacenamiento en búfer (lectura anticipada): el generador obtiene datos en bloques y arroja elementos individuales de cada bloque. De nuevo, el consumidor está separado de los detalles sangrientos.

Los generadores también pueden funcionar como coroutines. Puede pasar los datos en usando nextval=g.next(data) en el lado del 'consumidor' y data = yield(nextval) en el lado del generador. En este caso, el generador y sus valores de "intercambio" del consumidor. Incluso puede hacer que yield arroje una excepción dentro del contexto del generador: g.throw(exc) hace eso.

+0

El almacenamiento en búfer es un gran ejemplo, gracias. – systempuntoout

2

En todos los casos en que tengo algoritmos que leen algo, utilizo generadores exclusivamente.

¿Por qué?

La creación de capas en las reglas de filtrado, mapeo y reducción es mucho más fácil en un contexto de múltiples generadores.

Ejemplo:

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 

Mi preferencia es el uso de muchos pequeños generadores de manera que un pequeño cambio no es perjudicial para toda la cadena de proceso.

+0

¿Las funciones no funcionarían igual de bien? –

+2

Así que solo está usando las funciones del generador como una notación conveniente para los decoradores de iteradores. Creo que el ejemplo de Nick D es mucho mejor, ya que resalta el aspecto de continuación. – nikow

+0

@J. T. Hurley: No sé qué significa "igual de bueno", pero los generadores no crean resultados intermedios, donde las funciones en general sí lo hacen. Los generadores anidados son una especie de canal de reducción de mapas. –

Cuestiones relacionadas