2015-10-25 11 views
9
>>> non_iterable = 1 
>>> 5 in non_iterable 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
TypeError: 'int' object is not iterable 

>>> class also_non_iterable: 
...  def __contains__(self,thing): 
...   return True 

>>> 5 in also_non_iterable() 
True 
>>> isinstance(also_non_iterable(), Iterable) 
False 

Esiste un motivo per le rivendicazioni di parole chiave in per volere un oggetto iterabile quando ciò che realmente desidera è un oggetto che implementa __contains__?Perché la parola chiave 'in' afferma che ha bisogno di un oggetto iterabile?

risposta

13

Sostiene di volere un iterable perché, se la classe dell'oggetto non implementa un __contains__, allora in tenta di ripetere l'oggetto e controllare se i valori sono uguali ai valori che ne derivano.

un esempio per dimostrare che -

>>> class C: 
...  def __iter__(self): 
...   return iter([1,2,3,4]) 
>>> 
>>> c = C() 
>>> 2 in c 
True 
>>> 5 in c 
False 

Questo è spiegato in the documentation -

Per classi definite dall'utente che definiscono il metodo __contains__(), x in y è vero se e solo se y.__contains__(x) è vero.

Per classi definite dall'utente che non definiscono __contains__() ma definiscono __iter__(), x in y vale se un valore z con x == z viene prodotto, mentre l'iterazione di y. Se viene sollevata un'eccezione durante l'iterazione, è come se sollevasse quell'eccezione.

+0

Ha senso. Immagino che il messaggio di errore sia solo un po 'inesatto, quindi. –

+2

Non necessariamente. Se hai provato contiene prima, non è riuscito e sono tornati alle iterazioni successive, il bit di iterazione è il punto in cui un'eccezione potrebbe esplodere. Quindi la messaggistica è coerente con il punto di errore non recuperabile. –

+0

E sì, che è anche menzionato nella documentazione - * Se viene sollevata un'eccezione durante l'iterazione, è come se fosse stata sollevata quell'eccezione. *. –

1

Ci sono due diversi usi della in:

  • test se un contenitore presenta un valore (es. Argomento a sinistra implementa __contains__)
  • trasversale attraverso una sequenza (. Es argomento di destra è Iterable)
+0

Buon punto. Ma sarebbe molto inefficiente iterare attraverso una grande lista (uso libero qui) da un oggetto che aveva un \ __ contain__ come dire un ditt. O ancora di più un set.Quindi un test per l'appartenenza non dovrebbe essere trattato diversamente da una sequenza traversal e privilegio a contenere il controllo prima? –

3

C'è una ragione in parola chiave sostiene di volere un oggetto iterabile quando ciò che si vuole veramente è un oggetto che implementa __contains__?

x in thing e for x in thing sono strettamente correlati. Quasi tutto ciò che supporta x in thing segue la regola che x in thing è true se e solo se un ciclo for su thing trova un elemento uguale a x. In particolare, se un oggetto supporta l'iterazione ma non __contains__, Python utilizzerà l'iterazione come fallback per i test in.

Il messaggio di errore potrebbe indicare che è necessario __contains__, ma sarebbe errato quanto il messaggio corrente, poiché __contains__ non è strettamente necessario. Potrebbe dire che ha bisogno di un contenitore, ma non è immediatamente chiaro ciò che conta come contenitore. Ad esempio, dicts supporta in, ma chiamarli contenitori è discutibile. Il messaggio corrente, che dice che ha bisogno di un iterable, è preciso quanto le altre opzioni. Il suo vantaggio è che, in pratica, "è iterabile" è un controllo migliore di "è un contenitore" o "supporta __contains__" per determinare se gli oggetti reali supportano in.

+0

Sono correlati, sì, ma ancora due cose diverse. 'per x in anche_non_iterable()' genererà ancora un 'TypeError'. Quindi questo è un errore separato. Sembra abbastanza facile dire "l'oggetto non è iterabile o un contenitore" al posto dell'errore originale. –

Problemi correlati