2010-11-21 4 views
6

ich eine Funktion, die eine Liste von DB-Tabellen als Parameter bekommt und gibt eine Befehlsfolge auf diesen Tabellen ausgeführt werden, zB:Pythonic Weise Parameter zu überprüfen, ist eine Sequenz, aber nicht String

pg_dump(file='/tmp/dump.sql', 
     tables=('stack', 'overflow'), 
     port=5434 
     name=europe) 

Sollte etwas zurücksenden wie:

pg_dump -t stack -t overflow -f /tmp/dump.sql -p 5434 europe 

Dies geschieht mit tables_string='-t '+' -t '.join(tables).

Der Spaß beginnt, wenn die Funktion aufgerufen wird, mit: tables=('stackoverflow') (a string) statt tables=('stackoverflow',) (ein Tupel), das ergibt:

pg_dump -t s -t t -t a -t c -t k -t o -t v -t e -t r -t f -t l -t o -t w 
     -f /tmp/dump.sql -p 5434 europe 

Da die Zeichenfolge selbst wiederholt wird.

This SO question schlägt vor, verwendet auf den Typ, aber ich bin mir nicht sicher, es ist Pythonic genug, weil es die Ducktyp Konvention bricht.

Irgendwelche Einsichten?

Adam

+0

Sie werden einem Benutzer aufgerufene mit '(‚foo‘)' versehentlich statt '(‚foo‘,) ', oder? – orip

+0

@orip Das ist der Punkt: Benutzer könnten diesen Fehler machen, und ich möchte verhindern, dass es passiert. –

Antwort

6

In diesem Fall scheint es angebracht zu sein, den Typ zu verwenden - einen üblichen Missbrauch zu behandeln, der wegen der Eingabe von Enten rechtmäßig erscheint.

Eine andere Möglichkeit, diesen häufigen Fall zu behandeln, wäre, nach einem String zu suchen und ihn als Spezialfall korrekt zu behandeln.

Schließlich könnte man die Tabellennamen als Positionsparameter fördern vorbei, die dieses Szenario weniger wahrscheinlich machen würde:

def pg_dump(*tables, **kwargs): 
    file = kwargs['file'] 
    port = kwargs['port'] 
    name = kwargs['name'] 
    ... 

pg_dump('stack', 'overflow', file='/tmp/dump.sql', port=5434, name='europe') 
+0

+1 Schön. Ich denke, ich werde den Parameter testen und einfach eine Zeichenfolge in ein 1-Tupel konvertieren. Es würde die Benutzer glücklicher machen. –

0

Können Sie nicht eine Liste eher als ein Tupel verwenden?

+0

Löst das Problem nicht - wenn der Benutzer eine Zeichenfolge übergibt, schlägt es immer noch fehl. –

1

Eine gemeinsame Python Idiom zu erkennen, ob ein Argument ist eine Sequenz (eine Liste oder Tupel) oder ein String ist zu prüfen, ob es das __iter__ Attribut hat:

def func(arg): 
    if hasattr(arg, '__iter__'): 
     print repr(arg), 'has __iter__ attribute' 
    else: 
     print repr(arg), 'has no __iter__ attribute' 

func('abc') 
# 'abc' has no __iter__ 

func(('abc')) 
# 'abc' has no __iter__ 

func(('abc',)) 
# ('abc',) has __iter__ 

Wenn es nicht eine Folge ist, ist es auch üblich, sie in eine ändern, um den Rest des Codes (die nur mit einer Art zu tun hat, zu vereinfachen von Ding). In der Probe könnte es mit einem einfachen arg = [arg] gemacht worden sein.

+5

Nebenbemerkung: Zeichenfolgen, die kein '__iter__'-Attribut haben, waren eine Inkonsistenz und wurden in Python 3 geändert, wie es sein sollte. –

+0

@ Jim Brissom: Ich fand immer, dass dies inkonsistent war, da Zeichenfolgen nach allen Zeichenfolgen sind. Kennst du ein gutes PY3K-Idiom? Wie wäre es mit der Überprüfung, ob der 'type (arg)' eine String-Methode hat, wie 'lower'? – martineau

+1

können Sie nach 'not isinstance (foo, str)' suchen, wie in der Frage vorgeschlagen, mit der der Asker verbunden ist ('basestring' anstelle von' str' für Python <3) – orip

2

können Sie ABCs verwenden zu behaupten, dass ein Objekt iterable ist aber kein String:

from types import StringType 
from collections import Iterable 
assert isinstance(x, Iterable) and not isinstance(x, StringType) 
Verwandte Themen