2013-04-24 11 views
5

Chcę funkcji, która zwykle przyjmuje argument typu X, gdzie X jest skalarem, listą lub dyktatem, i zwraca listę X z tymi samymi kluczami wartości, na podstawie innych informacji.Najlepszy sposób na rozróżnienie między argumentami skalarnymi, listowymi i dict w Pythonie?

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

Moje pytanie brzmi: jak mogę wysłać między tymi trzema typami?


edytuj: Ciąg znaków należy interpretować jako skalar, a nie jako listę/iterowalną. Krotki należy interpretować jako iterowalne.

edit 2: Chcę pisać na maszynie, a nie ściśle pisać.

+1

co, jeśli obiekt może wykazywać zarówno zachowania listy, jak i dyktatu? – Meitham

+1

zachowanie w przypadku elementów podobnych do dictwa ma pierwszeństwo przed zachowaniem dla elementów iterowalnych. –

+0

To pytanie wymaga zmiany nazwy, ponieważ jest to najlepsze rozwiązanie do określania różnicy między dict i listą, a nie tylko między skalarem i niesubtelnym, jak sugeruje tytuł. Nadal pomijam to w wyszukiwaniach. – SystemParadox

Odpowiedz

3

To zależy od tego, jak ścisłe chcesz być przy swoim wkładzie. Podejście isinstance wymusza określenie typów do zaakceptowania (tj. Bez pisania kaczkami). Działa tak długo, jak użytkownicy przechodzą tylko w tych klasach lub podtypach tych klas. Możesz również spróbować rozróżnić parametry za pomocą obsługiwanych metod. Przykładem tego byłoby

Edit: dodano specjalny przypadek ciągów

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] 

Wybór kontroli przepływu zależy od tego jak elastyczna chcesz być, a także w jaki sposób chcesz yo kontrakt z dwuznaczny przypadki. Np. Czy ciąg jest uważany za sekwencję znaków lub skalar?

+0

Zobacz moje zmiany w pytaniu. ciąg jest skalarem. (dla moich celów) –

+0

Czy dopuszczalne jest stosowanie ciągów znaków specjalnych (i unicodes, przypuszczam!) i zostawiam wszystko inne na typie kaczym? W przeciwnym razie musisz sprecyzować, co dokładnie sprawia, że ​​napisy są tak wyjątkowe :-) – Felipe

+0

Tak ... dobrze jest napisać ciągi znaków specjalnych. Ciągi są ciągami. :-) –

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

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

elif isinstance(keydata,dict):#dictionary 

może? (Im prawdopodobnie brakuje kilka rodzajów) ...

+3

'str' jest interesującym przypadkiem, może działać zarówno jako skalar jak i iterowalny. –

+0

tak ... ale doszedłem do wniosku, że było to bliższe skalar dla potrzeb OP ... –

+0

o cholera, co utrudnia życie, ponieważ chcę traktować ciąg jako skalar, a nie jako iterowalny. –

2

Użyj nowego wymyślnych rzeczy :) od zbiorów przywozowych

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

Należy również rozważyć patrząc na module types

+1

'isinstance (''. Collections.Sequence)' jest również "True". –

+0

@MartijnPieters nie powinno być :) Powiedziałbym też, że krotka to skalar, ponieważ jest nieosiągalna :) – Meitham

+0

Nazwałbym je skalarami nie dlatego, że są nieosiągalne, ale dlatego, że są niezmienne, ale tak. –

5

musisz robić rzeczy we właściwej kolejności od str i dict typy są iterable.

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

Dzięki za wszystkie informacje!

skończyło się to zrobić, ponieważ miałem zrobić jakieś wstępne przetwarzanie z następujących keydata przed iteracji nad informacjami:

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

Wtedy nie trzeba się martwić o braku osiągnięcia jednego isinstance bo kogoś implementacja iterowalnej subklassed z czegoś nieoczekiwanego.

+1

Ale wtedy muszę się martwić, że ktoś implementuje metodę iterowalną za pomocą atrybutu lub metody 'lower()'. –