2013-04-24 2 views
5

Ich möchte eine Funktion, die normalerweise ein Argument vom Typ X übernimmt, wobei X entweder ein Skalar, eine Liste oder ein Diktat ist, und eine Liste von X mit denselben Schlüsselwerten basierend auf anderen Informationen zurückgibt.Die beste Methode, um in Python zwischen skalaren, Listen- und dict-Argumenten zu unterscheiden?

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

Meine Frage ist, wie kann ich zwischen den drei Arten versenden?


edit: Eine Zeichenfolge ist als Skalar zu interpretieren, nicht als Liste/iterierbar. Tupel sind als iterierbar zu interpretieren.

bearbeiten 2: Ich möchte Ente tippen, nicht streng Typisierung.

+1

Was ist, wenn ein Objekt sowohl das Verhalten einer Liste als auch eines Diktats anzeigen kann? – Meitham

+1

dann hat das Verhalten für dict-ähnliche Elemente Vorrang vor dem Verhalten für iterierbare Elemente –

+0

Diese Frage muss umbenannt werden, da sie die beste ist, um den Unterschied zwischen dict und list zu bestimmen, nicht nur zwischen skalar und nicht skalar. Ich überspringe es bei Suchanfragen. – SystemParadox

Antwort

3

Es hängt davon ab, wie streng Sie wollen mit Ihrer Eingabe sein. Die isinstance-Methode zwingt Sie, die zu akzeptierenden Typen anzugeben (z. B. keine Duck-Typisierung). Es funktioniert, solange Ihre Benutzer nur diese Klassen oder Subtypen dieser Klassen übergeben. Sie können auch versuchen, Parameter anhand der von ihnen unterstützten Methoden zu unterscheiden. Ein Beispiel hierfür wäre

bearbeiten sein: hinzugefügt, um den Sonderfall für Streicher

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] 

Die Wahl der Steuerungsablauf hängt davon ab, wie flexibel Sie sein wollen, und auch, wie Sie wollen yo Deal mit mehrdeutigen Fälle. ZB wird eine Zeichenfolge als eine Zeichenfolge oder ein Skalar betrachtet?

+0

sehe meine Änderungen in der Frage. Zeichenfolge ist ein Skalar. (für meine Zwecke) –

+0

Wäre es akzeptabel, Sonderzeichenfolgen (und Unicodes, nehme ich an!) und lassen Sie alles andere enten-typed? Ansonsten müssen Sie genauer spezifizieren, was genau Strings so besonders macht :-) – Felipe

+0

Ja ... es ist ok für Sonderzeichenfolgen. Strings sind Strings. :-) –

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

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

elif isinstance(keydata,dict):#dictionary 

vielleicht ?? (Im fehlt wahrscheinlich ein paar Typen) ...

+3

'str' ist ein interessanter Fall, es kann sowohl als Skalar als auch als iterable fungieren. –

+0

yeah ... aber ich dachte, es wäre näher an einem Skalar für OPs Bedürfnisse ... –

+0

oh Mist, das macht das Leben schwieriger, weil ich eine Zeichenfolge als Skalar behandeln möchte, nicht als iterierbar. –

2

Verwenden Sie die neue fancy stuff :) von Import Sammlungen

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

Sie sollten auch am types Modul suchen betrachten

+1

'isinstance (''. Collections.Sequence)' ist auch 'True'. –

+0

@MartijnPieters sollte es nicht sein :) Ich würde auch sagen, ein Tupel ist ein Skalar, da es hashable ist :) – Meitham

+0

Ich würde sie Skalare nicht nennen, weil sie hashable sind, sondern weil sie unveränderlich sind, aber ja. –

5

Sie müssen die Dinge in der richtigen Reihenfolge tun, da str und dict Typen sind iterable.

0

Danke für alle Informationen!

landete ich dies zu tun, da ich von der einige Vorverarbeitung zu tun hatte keydata vor, die Informationen iterieren:

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

Dann müssen Sie sich niemals Sorgen über eine isinstance wegen jemand versagt eine iterierbare Unterklasse von etwas Unerwartetem implementieren.

+1

Aber dann muss ich mir Sorgen machen, dass jemand ein iterable mit einem 'lower() 'Attribut oder Methode implementiert. –