Domanda

Questo è quello che faccio normalmente al fine di accertare che l'ingresso è un list / tuple - ma non un str. Perché molte volte sono incappato insetti in cui una funzione passa un oggetto str per errore, e la funzione di destinazione non for x in lst assumendo che lst è in realtà un list o tuple.

assert isinstance(lst, (list, tuple))

La mia domanda è:? C'è un modo migliore per raggiungere tale obiettivo

È stato utile?

Soluzione

In Python 2 solo (non Python 3):

assert not isinstance(lst, basestring)

è in realtà ciò che si vuole, altrimenti vi perderete un sacco di cose che si comportano come liste, ma non sono sottoclassi di list o tuple.

Altri suggerimenti

Ricordate che in Python vogliamo usare "duck typing". Quindi, tutto ciò che si comporta come un elenco può essere trattata come una lista. Quindi, non controlla per il tipo di un elenco, basta vedere se agisce come una lista.

Ma le stringhe si comportano come un elenco troppo, e spesso questo non è quello che vogliamo. Ci sono momenti in cui è ancora un problema! Quindi, controllare in modo esplicito per una stringa, ma poi usare la tipizzazione anatra.

Ecco una funzione che ho scritto per il divertimento. Si tratta di una versione speciale di repr() che consente di stampare 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

Questa è pulito ed elegante, nel complesso. Ma che cosa è che il check isinstance() facendo lì? Questo è una specie di hack. Ma è essenziale.

Questa funzione chiama se stessa ricorsivamente su tutto ciò che si comporta come una lista. Se non avessimo gestire la stringa speciale, allora sarebbe stato trattato come una lista, e contempla un carattere alla volta. Ma poi la chiamata ricorsiva avrebbe cercato di trattare ogni personaggio come una lista - e che avrebbe funzionato! Anche una stringa di un carattere funziona come una lista! La funzione dovrebbe continuare a chiamare se stessa in modo ricorsivo fino overflow dello stack.

Funzioni come questo, che dipendono da ogni chiamata ricorsiva abbattere il lavoro da fare, avere in stringhe speciali case - perché non è possibile abbattere una stringa di sotto del livello di una stringa di un carattere, e persino una stringa di un carattere si comporta come una lista.

Nota: il try / except è il modo più pulito per esprimere le nostre intenzioni. Ma se questo codice fosse in qualche modo time-critical, si potrebbe desiderare di sostituirlo con una sorta di test per vedere se arg è una sequenza. Invece di testare il tipo, probabilmente dovremmo testare i comportamenti. Se si ha un metodo .strip(), è una stringa, quindi non considerarlo una sequenza; altrimenti, se è indicizzabile o iterabile, si tratta di 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 assegno di __getslice__() ma ho notato che nella documentazione del modulo collections, il metodo interessante è __getitem__(); questo ha un senso, è così che si indice un oggetto. Che sembra più fondamentale di __getslice__() così ho cambiato quanto sopra.

H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

Per Python 3:

import collections.abc

if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
    print("obj is a sequence (list, tuple, etc) but not a string")
  

Modificato nella versione 3.3: SPOSTATO Collezioni classi astratte di base al modulo collections.abc. Per compatibilità all'indietro, continueranno ad essere visibili in questo modulo così fino alla versione 3.8 dove smetterà di funzionare.

Per Python 2:

import collections

if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
    print "obj is a sequence (list, tuple, etc) but not a string or unicode"

Python con sapore di PHP:

def is_array(var):
    return isinstance(var, (list, tuple))

In linea generale, il fatto che una funzione che itera su un oggetto lavora sulle corde così come tuple e liste è più caratteristica di bug. Certamente possono uso isinstance o digitando anatra per controllare un argomento, ma perché dovrebbe?

Che suona come una domanda retorica, ma non lo è. La risposta alla domanda "perché dovrei controllare il tipo di argomento?" sta probabilmente andando a suggerire una soluzione al vero problema, non il problema percepito. Perché è un errore quando una stringa viene passata alla funzione? Inoltre: se si tratta di un bug quando una stringa viene passato a questa funzione, è anche un bug se qualche altro non-list / tupla iterabile viene passato ad esso? Perché, o perché no?

Credo che la risposta più comune alla domanda è probabile che sia che gli sviluppatori che scrivono f("abc") si aspettano la funzione di comportarsi come se avessero scritto f(["abc"]). Probabilmente ci sono circostanze in cui ha più senso per proteggere gli sviluppatori da se stessi di quanto non faccia per supportare il caso d'uso di iterazione attraverso i caratteri di una stringa. Ma mi piacerebbe pensare a lungo e duramente su di esso prima.

Prova questo per migliorare la leggibilità e le migliori pratiche:

python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

Speranza che aiuta.

L'oggetto str non ha un attributo __iter__

>>> hasattr('', '__iter__')
False 

in modo da poter fare un controllo

assert hasattr(x, '__iter__')

e questo sarà anche sollevare una bella AssertionError per qualsiasi altro oggetto non iterabile troppo.

Modifica: Come Tim cita nei commenti, questo funziona solo in Python 2.x, non 3.x

Questa non è destinato a rispondere direttamente il PO, ma ho voluto condividere alcune idee legate.

Ero molto interessato a @steveha risposta di cui sopra, che sembrava dare un esempio in cui duck typing sembra rompere. A pensarci bene, però, il suo esempio suggerisce che duck typing è difficile da conformarsi, ma lo fa non suggeriscono che str merita alcun trattamento speciale.

Dopo tutto, un tipo non-str (ad esempio, un tipo definito dall'utente che mantiene alcune complicate strutture ricorsive) può causare @steveha funzione srepr per causare una ricorsione infinita. Mentre questo è certamente piuttosto improbabile, non possiamo ignorare questa possibilità. Pertanto, piuttosto che speciale involucro str in srepr, dobbiamo chiarire che cosa vogliamo srepr fare quando un infinito di ricorsione risultati.

Può sembrare che un approccio ragionevole è quello di rompere semplicemente la ricorsione in srepr momento list(arg) == [arg]. Ciò, infatti, risolvere completamente il problema con str, senza alcuna isinstance.

Tuttavia, davvero complicata struttura ricorsiva può causare un ciclo infinito dove list(arg) == [arg] non accade mai. Pertanto, mentre il controllo di cui sopra è utile, non è sufficiente. Abbiamo bisogno di qualcosa di simile a un limite rigido sulla profondità di ricorsione.

Il mio punto è che se avete intenzione di gestire tipi di argomenti arbitrari, la manipolazione str tramite digitazione anatra è molto, molto più facile di gestire i tipi più generali si può (teoricamente) incontro. Quindi, se si sente la necessità di escludere casi str, si dovrebbe invece chiedere che l'argomento è un esempio di uno dei pochi tipi specificati in modo esplicito.

Trovo una tale funzione denominata is_sequence in tensorflow .

def is_sequence(seq):
  """Returns a true if its input is a collections.Sequence (except strings).
  Args:
    seq: an input sequence.
  Returns:
    True if the sequence is a not a string and is a collections.Sequence.
  """
  return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))

E ho verificato che soddisfi le vostre esigenze.

Lo faccio nei miei casi di test.

def assertIsIterable(self, item):
    #add types here you don't want to mistake as iterables
    if isinstance(item, basestring): 
        raise AssertionError("type %s is not iterable" % type(item))

    #Fake an iteration.
    try:
        for x in item:
            break;
    except TypeError:
        raise AssertionError("type %s is not iterable" % type(item))

Non testato su generatori, penso che si sono lasciati alla prossima 'resa' se approvata in un generatore, che può rovinare tutto a valle. Ma poi di nuovo, questo è un 'unittest'

semplice modo ... utilizzando any e isinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True

In maniera "duck typing", come su

try:
    lst = lst + []
except TypeError:
    #it's not a list

o

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

, rispettivamente. Questo evita la roba isinstance / hasattr introspezione.

Si potrebbe anche verificare viceversa:

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

Tutte le varianti in realtà non cambiano il contenuto della variabile, ma implicano un cambiamento di sesso. Non sono sicuro se questo potrebbe essere auspicabile in alcune circostanze.

È interessante notare che, con l'assegnazione "a posto" += non TypeError sarebbe sollevata in ogni caso se lst è un (non un tuple ). È per questo che l'assegnazione è fatto in questo modo. Forse qualcuno può far luce sul perché.

Basta fare questo

if type(lst) in (list, tuple):
    # Do stuff

Python 3 ha questo:

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

Quindi, per verificare la presenza di entrambe le liste e tuple, sarebbe:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

I tendono a fare questo (se davvero, davvero dovuto):

for i in some_var:
   if type(i) == type(list()):
       #do something with a list
   elif type(i) == type(tuple()):
       #do something with a tuple
   elif type(i) == type(str()):
       #here's your string
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top