2013-04-24 20 views
5

Voglio una funzione che normalmente contiene un argomento di tipo X in cui X è uno scalare, un elenco o un dict e restituisce un elenco di X con gli stessi valori chiave, in base ad altre informazioni.Il modo migliore per distinguere tra gli argomenti scalari, elenco e dict in Python?

def foo(info, k): 
    return [bar(item,k) for item in processInfo(info)] 

def bar(item, keydata): 
    # pseudocode follows. 
    # What we want to do is return an output of parallel type to the input key k, 
    # using the key data to lookup information from the input item. 
    if keydata is a scalar: 
     return item[keydata] 
    elif keydata is a list: 
     return [item[k] for k in keydata] 
    elif keydata is a dict: 
     return dict((k,item[v]) for (k,v) in keydata.iteritems()) 
    else: 
     raise ValueError('bar expects a scalar, list, or dict') 

La mia domanda è, come posso effettuare la spedizione tra i tre tipi?


modifica: una stringa deve essere interpretata come scalare, non come lista/iterabile. Le tuple devono essere interpretate come iterabili.

modifica 2: Voglio digitazione anatra, non tipizzazione rigorosa.

+1

cosa succede se un oggetto può mostrare entrambi i comportamenti di una lista e un dict? – Meitham

+1

quindi il comportamento per elementi dict-like ha la precedenza sul comportamento per gli elementi iterabili –

+0

Questa domanda deve essere rinominata, poiché è la migliore per determinare la differenza tra dict e list, non solo tra scalare e nonscalare come implica il titolo. Continuo a saltarci sopra nelle ricerche. – SystemParadox

risposta

3

Dipende da quanto rigorosa vuoi essere con il tuo contributo. L'approccio isinstance ti obbliga a specificare i tipi da accettare (ad es., Senza battitura d'anatra). Funziona fintanto che i tuoi utenti passano solo in quelle classi o sottotipi di quelle classi. Puoi anche provare a distinguere i parametri con i metodi che supportano. Un esempio di questo sarebbe

Edit: aggiunto il caso speciale per le stringhe

if isinstance(keydata, basestring): 
    # special case to avoid considering strings as containers 
    # for python 3.x use str instead of basestring 
    return item[keydata] 
try: 
    return dict((k,item[v]) for (k,v) in keydata.iteritems()) 
except AttributeError: 
    # it's not a dict-like 
    pass 
try: 
    return [item[k] for k in keydata] 
except TypeError: 
    # it's not iterable 
return item[keydata] 

La scelta del flusso di controllo dipende da quanto flessibile si vuole essere, e anche come si desidera yo accordo con ambigui casi. Ad esempio, una stringa è considerata una sequenza di caratteri o uno scalare?

+0

vedere le mie modifiche nella domanda. la stringa è uno scalare. (per i miei scopi) –

+0

Sarebbe accettabile per le stringhe del caso speciale (e unicode, presumo!) e lasciare tutto il resto digitato a macchina? Altrimenti è necessario specificare più dettagliatamente cosa rende le stringhe così speciali :-) – Felipe

+0

Sì ... va bene alle stringhe del caso speciale. Le stringhe sono stringhe. :-) –

0
if isinstance(keydata,(int,float,str)): #scalar 

elif isinstance(keydata,(list,tuple)):#iterable 

elif isinstance(keydata,dict):#dictionary 

forse ?? (Im probabilmente manca alcuni tipi) ...

+3

'str' è un caso interessante, può agire sia come scalare che come iterabile. –

+0

sì ... ma ho pensato che fosse più vicino a uno scalare per le esigenze dell'OP ... –

+0

oh merda, che rende la vita più difficile, perché voglio trattare una stringa come scalare, non come un iterabile. –

2

Utilizzare il nuovo roba di fantasia :) da collezioni di importazione

>>> isinstance([], collections.Sequence) 
True 
>>> isinstance({}, collections.Mapping) 
True 

Si dovrebbe anche prendere in considerazione guardando il modulo types

+1

'isinstance (''. Collections.Sequence)' è 'True' too. –

+0

@MartijnPieters non dovrebbe essere :) Vorrei anche dire che una tupla è uno scalare poiché è lavabile :) – Meitham

+0

Io li chiamerei scalari non perché sono lavabili ma perché sono immutabili, ma sì. –

5

Hai bisogno di fare le cose nel giusto ordine dal str e dict tipi sono iterabile.

from collections import Iterable, Mapping # in Python 3 use from collections.abc 

def bar(item, keydata): 
    if isinstance(keydata, Mapping): 
     return {k: item[v] for (k,v) in keydata.iteritems()} 
    elif isinstance(keydata, Iterable) and not isinstance(keydata, str): 
     return [item[k] for k in keydata] 
    return item[keydata] 
0

Grazie per tutte le informazioni!

ho finito per fare questo, dal momento che ho dovuto fare un po 'di pre-elaborazione del keydata prima iterazione sulle informazioni:

def asKVlists(keydata): 
    # return a tuple (keys, values, isScalar) 
    # where keys and values are iterable 
    if not isinstance(keydata, basestring): 
     # check for dict behavior: 
     try: 
      return zip(*keydata.iteritems()) + [False] 
     except AttributeError: 
      pass 

     # otherwise check for list behavior 
     # make sure we can iterate over it 
     try: 
      iter(keydata) 
      return (None, keydata, False) 
     except TypeError: 
      pass 
    return (None, (keydata,), True) 
0
def is_scalar(x): 
    return hasattr(x, 'lower') or not hasattr(x, '__iter__') 

Allora non dovrai mai preoccuparti di non aver un isinstance a causa di qualcuno implementando una sottoclasse iterabile da qualcosa di inaspettato.

+1

Ma poi devo preoccuparmi di qualcuno che implementa un iterabile con un attributo o metodo 'lower()'. –

Problemi correlati