Ricorda che in Python vogliamo usare la "tipizzazione anatra". Quindi, tutto ciò che si comporta come un elenco può essere trattato come un elenco. Quindi, non controllare il tipo di un elenco, vedi solo se si comporta come un elenco.
Ma anche le stringhe si comportano come un elenco e spesso non è quello che vogliamo. Ci sono momenti in cui è persino un problema! Quindi, controlla esplicitamente una stringa, ma poi usa la tipizzazione duck.
Ecco una funzione che ho scritto per divertimento. È una versione speciale repr()
che stampa qualsiasi sequenza tra parentesi angolari ('<', '>').
def srepr(arg):
if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
return repr(arg)
try:
return '<' + ", ".join(srepr(x) for x in arg) + '>'
except TypeError: # catch when for loop fails
return repr(arg) # not a sequence so just return repr
Questo è pulito ed elegante, nel complesso. Ma che isinstance()
ci fa quel controllo? È una specie di hack. Ma è essenziale.
Questa funzione si chiama ricorsivamente su tutto ciò che si comporta come un elenco. Se non gestissimo la stringa in modo speciale, verrebbe trattata come una lista e divisa un carattere alla volta. Ma poi la chiamata ricorsiva proverebbe a trattare ogni personaggio come un elenco - e funzionerebbe! Anche una stringa di un carattere funziona come un elenco! La funzione continuerebbe a richiamare se stessa in modo ricorsivo fino allo overflow dello stack.
Funzioni come questa, che dipendono da ogni chiamata ricorsiva che interrompe il lavoro da svolgere, devono stringhe di casi speciali, poiché non è possibile suddividere una stringa al di sotto del livello di una stringa di un carattere e persino una -character stringa si comporta come un elenco.
Nota: il try
/ except
è il modo più pulito per esprimere le nostre intenzioni. Ma se questo codice fosse in qualche modo critico nel tempo, potremmo volerlo sostituire con una sorta di test per vedere se arg
è una sequenza. Invece di testare il tipo, dovremmo probabilmente testare i comportamenti. Se ha un .strip()
metodo, è una stringa, quindi non considerarla una sequenza; altrimenti, se è indicizzabile o iterabile, è una sequenza:
def is_sequence(arg):
return (not hasattr(arg, "strip") and
hasattr(arg, "__getitem__") or
hasattr(arg, "__iter__"))
def srepr(arg):
if is_sequence(arg):
return '<' + ", ".join(srepr(x) for x in arg) + '>'
return repr(arg)
EDIT: originariamente ho scritto quanto sopra con un controllo per __getslice__()
ma ho notato che nella collections
documentazione del modulo, il metodo interessante è __getitem__()
; questo ha senso, è così che indicizzi un oggetto. Sembra più fondamentale di __getslice__()
così, ho cambiato quanto sopra.