2014-12-15 9 views
8

Ho una lista di oggetti denominati articoli. Ogni oggetto ha una proprietà stato e una proprietà bambini, che è un altro elenco di oggetti. E ogni oggetto figlio ha anche una proprietà denominata stato. Quello che voglio sapere è se ogni articolo e i loro figli sono negli stati felici o allegro.È possibile annidare la funzione all?

ho fatto con tutti (solo analizzare gli stati degli elementi):

if all(item.state in ['happy', 'cheerful'] for item in items): 
    pass 

vorrei sapere quale è il modo migliore per fare lo stesso con non solo gli elementi, ma anche i bambini.

risposta

5

siete alla ricerca di un po 'di ricorsione qui:

def is_happy(items): 
    return all(item.state in ['happy', 'cheerful'] for item in items) and all(is_happy(item.childs) for item in items) 

Come @tobias_k sottolineato questo dovrebbe essere più veloce dato che itera solo una volta su oggetti:

def is_happy(items): 
    return all(item.state in ['happy', 'cheerful'] and is_happy(item.childs) for item in items) 

È almeno più leggibile.

Nel caso in cui si abbiano solo due livelli di oggetti, anche un semplice potrebbe fare il trucco.

def is_happy(items): 
    happy_children = True 
    for item in items: 
     if any(child.state not in ['happy', 'cheerful'] for child in item): 
      happy_children = False 
      break 
    return all(item.state in ['happy', 'cheerful'] for item in items) and happy_children 
+0

Preferisco un semplice ciclo 'for' in questo caso semplice, a meno che OP non abbia molti altri livelli da trattare. – jamylak

+0

o più breve: 'tutto (item.state in ['happy', 'cheerful'] e is_happy (item.childs per l'articolo negli articoli)' –

+0

Penso che OP stia parlando di più di un livello. –

1

Primo passo: appiattire l'elenco delle voci:

def flatten(items): 
    for item in items: 
     yield item 
     for child in getattr(item, 'children',()): 
      yield child 

o utilizzando Python 3.4+:

def flatten(items): 
    for item in items: 
     yield item 
     yield from getattr(item, 'children',()) 

E ora è possibile scorrere gli elementi appiattiti utilizzando all(..) o qualche altro modo :

is_happy = lambda item: getattr(item, 'state') in ('happy', 'cheerful') 
are_happy = lambda items: all(map(is_happy, flatten(items))) 
1

ricorsione è tuo amico qui

def happy(x): 
    return (x.state in ('happy', 'cheerful') and 
      all(happy(xc) for xc in x.children)) 
2

Credo che questo sarebbe il modo giusto:

if all(item.state in ['happy', 'cheerful'] and all(c.state in ['happy', 'cheerful'] for c in item.childs) for item in items): 
    pass 
+0

'all (item.childs.state in ['happy', 'cheerful'])' Dubito che funzionerà. 'item.childs' è una sorta di lista e non ha attributi' state'. Immagino che questo dovrebbe essere "tutto (c.state in [...] per c in item.childs" –

+0

Hai ragione. Aggiornato la mia soluzione. Grazie. –

1

Questa è probabilmente la cosa migliore fatta manualmente.

def valid(item): 
    return item.state in ['happy', 'cheerful'] 

for item in items: 
    if not (valid(item) and all(valid(child) for child in item)): 
     break 
else: 
    # success 

Cambiare l'espressione del generatore per funzionare con questo è possibile ma lo rende un po 'hacky.

if all(child.state in ['happy', 'cheerful'] for item in items for child in item+[item]): 
    pass 

Quindi, per rispondere alla tua domanda, sì, è possibile annidare la funzione all e thats come si potrebbe fare se si voleva davvero.

Problemi correlati