una - unire due liste python




Come verificare se un oggetto è una lista o una tupla(ma non una stringa)? (12)

Questo è quello che faccio normalmente per accertarmi che l'input sia una list / tuple - ma non uno str . Perché molte volte mi sono imbattuto in bug in cui una funzione passa per errore un oggetto str , e la funzione target fa for x in lst assumendo che lst sia effettivamente una list o una tuple .

assert isinstance(lst, (list, tuple))

La mia domanda è: esiste un modo migliore per raggiungere questo obiettivo?


Fallo e basta

variable = pd.Series(variable).tolist()

In generale, il fatto che una funzione che itera su un oggetto funzioni su stringhe così come tuple e liste è più una caratteristica che un bug. Puoi certamente usare isinstance o digitazione anatra per controllare un argomento, ma perché dovresti?

Sembra una domanda retorica, ma non lo è. La risposta a "perché dovrei controllare il tipo dell'argomento?" probabilmente suggerirà una soluzione al problema reale, non al problema percepito. Perché è un bug quando una stringa viene passata alla funzione? Inoltre: se si tratta di un bug quando una stringa viene passata a questa funzione, è anche un bug se viene passato ad altri non-list / tuple iterable? Perché o perché no?

Penso che la risposta più comune alla domanda sia che gli sviluppatori che scrivono f("abc") si aspettano che la funzione si comporti come se avessero scritto f(["abc"]) . Ci sono probabilmente circostanze in cui ha più senso proteggere gli sviluppatori da se stessi che non supportare il caso d'uso di iterare attraverso i caratteri in una stringa. Ma ci penserei a lungo e duramente prima.


Lo faccio nei miei testicoli.

>>> 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

Non testato sui generatori, penso che tu sia rimasto al prossimo "rendimento" se passato in un generatore, che potrebbe rovinare tutto a valle. Ma poi di nuovo, questo è un 'unittest'


Nel modo di "battere a macchina", che ne dici?

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

o

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

rispettivamente. Questo evita l'introspezione isinstance / hasattr .

Potresti anche controllare viceversa:

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

Tutte le varianti non cambiano effettivamente il contenuto della variabile, ma implicano una riassegnazione. Non sono sicuro che questo potrebbe essere indesiderabile in alcune circostanze.

È interessante notare che con l'assegnazione "in place" += no TypeError verrebbe sollevato in ogni caso se lst è una lista (non una tupla ). Ecco perché il compito è fatto in questo modo. Forse qualcuno può far luce sul perché.


Prova questo per la leggibilità e le migliori pratiche:

python2

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

python3

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))

Spero che sia d'aiuto.


Python 3 ha questo:

from typing import List, Tuple

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

Quindi, per controllare sia liste che tuple, sarebbe:

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

Questo non ha lo scopo di rispondere direttamente all'OP, ma ho voluto condividere alcune idee correlate.

Ero molto interessato alla risposta di @steveha qui sopra, che sembrava dare un esempio in cui la tipizzazione anatra sembra rompere. A pensarci bene, tuttavia, il suo esempio suggerisce che è difficile conformarsi alla digitazione anatra, ma non suggerisce che merita alcun trattamento speciale.

Dopotutto, un tipo non- str (ad esempio, un tipo definito dall'utente che mantiene alcune complicate strutture ricorsive) può causare che la funzione sstepr di srepr causi una ricorsione infinita. Mentre questo è certamente piuttosto improbabile, non possiamo ignorare questa possibilità. Pertanto, piuttosto che srepr in srepr , dovremmo chiarire che cosa vogliamo che srepr faccia quando una ricorsione infinita risulta.

Può sembrare che un approccio ragionevole sia semplicemente interrompere la ricorsione in srepr the moment list(arg) == [arg] . Questo, infatti, risolverebbe completamente il problema con str , senza alcuna isinstance .

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

Il mio punto è che se si intende gestire tipi di argomenti arbitrari, la gestione dello str tramite la digitazione anatra è molto, molto più semplice rispetto alla gestione dei tipi più generali che si possono (teoricamente) incontrare. Quindi, se senti la necessità di escludere le istanze di str , dovresti invece chiedere che l'argomento sia un'istanza di uno dei pochi tipi che specifichi esplicitamente.


Ricorda che in Python vogliamo usare "duck typing". Quindi, tutto ciò che si comporta come una lista può essere trattato come una lista. Quindi, non controllare il tipo di una lista, basta vedere se si comporta come una lista.

Ma le stringhe si comportano come una lista e spesso non è quello che vogliamo. Ci sono momenti in cui è persino un problema! Quindi, controlla esplicitamente una stringa, ma poi usa la digitazione anatra.

Ecco una funzione che ho scritto per divertimento. È una versione speciale di 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 cosa è il controllo isinstance() ? È una specie di hack. Ma è essenziale.

Questa funzione si chiama in modo ricorsivo su tutto ciò che funge da elenco. Se non abbiamo gestito la stringa in modo speciale, sarebbe trattata come una lista e dividere un carattere alla volta. Ma poi la chiamata ricorsiva cercherebbe di trattare ogni personaggio come una lista - e funzionerebbe! Anche una stringa di un solo carattere funziona come una lista! La funzione continuava a chiamare se stessa in maniera ricorsiva fino allo .

Funzioni come questa, che dipendono da ogni chiamata ricorsiva che suddivide il lavoro da eseguire, devono stringhe di casi speciali - perché non è possibile scomporre una stringa al di sotto del livello di una stringa di un solo carattere, e persino una stringa -character string agisce come una lista.

Nota: il try / except è il modo più pulito per esprimere le nostre intenzioni. Ma se questo codice fosse in qualche modo critico dal punto di vista temporale, potremmo voler sostituirlo con una sorta di test per vedere se arg è una sequenza. Piuttosto che testare il tipo, dovremmo probabilmente testare i comportamenti. Se ha un metodo .strip() , è 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 documentazione del modulo delle collections , il metodo interessante è __getitem__() ; questo ha senso, è così che indichi un oggetto. Sembra più fondamentale di __getslice__() quindi ho modificato quanto sopra.


Solo in python 2 (non python 3):

assert not isinstance(lst, basestring)

In realtà è ciò che vuoi, altrimenti ti perderai un sacco di cose che si comportano come liste, ma non sono sottoclassi di list o tuple .


Tendo a farlo (se davvero, davvero dovevo farlo):

 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 

modo più semplice ... usando any ed isinstance

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

H = "Hello"

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




assert