2009-06-05 20 views
5

È possibile creare un attributo su un oggetto generatore?python: attributi su un oggetto generatore

Ecco un esempio molto semplice:

def filter(x): 
    for line in myContent: 
     if line == x: 
      yield x 

Ora dire che ho un sacco di questi generatore di filtrare gli oggetti galleggianti intorno ... forse alcuni di loro sono anonimi ... voglio tornare più tardi e interrogare per quello per cui stanno filtrando. C'è un modo in cui posso a) interrogare l'oggetto generatore per il valore di x o b) impostare un attributo con il valore di x che posso interrogare in seguito?

Grazie

risposta

6

Sfortunatamente, gli oggetti generatore (i risultati restituiti dalla chiamata a una funzione di generatore) non supportano l'aggiunta di attributi arbitrari. È possibile aggirare il problema in un certo senso utilizzando un dt esterno indicizzato dagli oggetti del generatore, poiché tali oggetti sono utilizzabili come chiavi in ​​un dettato. Allora, dove faresti come di fare, dire:

a = filter(23) 
b = filter(45) 
... 
a.foo = 67 
... 
x = random.choice([a,b]) 
if hasattr(x, 'foo'): munge(x.foo) 

si possono invece fare:

foos = dict() 
a = filter(23) 
b = filter(45) 
... 
foos[a] = 67 
... 
x = random.choice([a,b]) 
if x in foos: munge(foos[x]) 

Per quanto elaborato, utilizzare una classe invece di un generatore (una o più della classe di i metodi possono essere generatori, dopo tutto).

16

Sì.

class Filter(object): 
    def __init__(self, content): 
     self.content = content 
    def __call__(self, someParam): 
     self.someParam = someParam 
     for line in self.content: 
      if line == someParam: 
       yield line 
2

No. Non è possibile impostare attributi arbitrari sui generatori.

Come S. Lott fa notare, si può avere un oggetto che sembra come un generatore, e atti come un generatore. E se sembra un'anatra e si comporta come un'anatra, hai già la definizione stessa di dattilografia, proprio lì.

Non supporta gli attributi del generatore come gi_frame senza i metodi proxy appropriati, tuttavia.

0

Pensando al problema, ci è un modo di avere generatori portare un set di attributi. È un po 'pazzesco - consiglio vivamente il suggerimento di Alex Martelli invece di questo - ma potrebbe essere utile in alcune situazioni.

my_content = ['cat', 'dog days', 'catfish', 'dog', 'catalog'] 

def filter(x): 
    _query = 'I\'m looking for %r' % x 

    def _filter(): 
     query = yield None 
     for line in my_content: 
      while query: 
       query = yield _query 

      if line.startswith(x): 
       query = yield line 

     while query: 
      query = yield _query 

    _f = _filter() 
    _f.next() 
    return _f 

for d in filter('dog'): 
    print 'Found %s' % d 

cats = filter('cat') 
for c in cats: 
    looking = cats.send(True) 
    print 'Found %s (filter %r)' % (c, looking) 

Se si vuole chiedere il generatore che cosa è il filtraggio, basta chiamare send con un valore che restituisce true. Naturalmente, questo codice è probabilmente troppo intelligente della metà. Usare con cautela.

3

Se si vuole interrogarli a scopo di debug, quindi la seguente funzione vi aiuterà:

import inspect 

def inspect_generator(g): 
    sourcecode = open(g.gi_code.co_filename).readlines() 
    gline = g.gi_code.co_firstlineno 
    generator_code = inspect.getblock(sourcecode[gline-1:]) 

    output = "Generator %r from %r\n" % (g.gi_code.co_name, g.gi_code.co_filename) 
    output += "".join("%4s: %s" % (idx+gline, line) for idx, line in enumerate(generator_code)) 

    output += "Local variables:\n" 
    output += "".join("%s = %r\n" % (key,value) for key,value in g.gi_frame.f_locals.items()) 

    return output 

print inspect_generator(filter(6)) 
"""Output: 
Generator 'filter' from 'generator_introspection.py' 
    1: def filter(x): 
    2:  for line in myContent: 
    3:   if line == x: 
    4:    yield x 
Local variables: 
x = 6 
""" 

Se si vuole interrogare loro di implementare la funzionalità poi classi che implementano il protocollo iteratore sono probabilmente una migliore idea.

0

Mi rendo conto che questa è una risposta molto tardiva, ma ...

Invece di memorizzare e poi la lettura di alcuni attributo aggiuntivo, il codice potrebbe poi basta ispezionare variabili (s) del generatore, utilizzando:

filter.gi_frame.f_locals

Credo Formiche Aasma accennato a questo.

+0

Ho provato il tuo suggerimento di ispezionare le variabili del generatore. Funziona fino a quando 'StopIteration' non è stato ancora raggiunto. Dopo di ciò, 'gi_frame' diventa' None'. – bli

Problemi correlati