Seite 1 von 1

Python *args, **keywords

Verfasst: Mittwoch 18. Februar 2009, 11:06
von danims
Hallo zusammen

Ich habe mich schon länger gefragt, was es mit den *args und **kw an sich hat, die man so in manchen Beispielen sieht.

Ich hab eine frage dazu, zuerst aber das Beispiel aus der Python Doku:

Code: Alles auswählen

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, '?'
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print '-'*40
    for kw in keywords.keys(): print kw, ':', keywords[kw]
So, also die Funktion cheeseshop hat ein benötigtes Argument "kind", dann eine anzahl n argumente und eine anzahl n an keywords.

soweit sogut. Ich habe bisher immer funktionen gemacht, bei denen ich nur jeweils ein argument brauchte. Jetzt aber komme ich in die situation, dass ich einer funktion mehrere mögliche optionen mitgeben möchte.

es geht konkret um eine funktion, die einen benutzer in eine datenbank hinzufügt:

Code: Alles auswählen

def addUser(username, password, **kw):
   ...
als beispiel für ein optionales keyword nehme ich "active", welcher sagt ob der benutzer sich einloggen kann oder nicht. wenn ich in der funktion addUser() aber versuche, auf kw['active'] zuzugreifen, kriege ich natürlich eine fehlermeldung, wenn diese nicht übergeben wurde.

gibt es dazu eine saubere lösung, dies zu machen? oder ist das beispiel in der python doku so "empfohlen"?

Verfasst: Mittwoch 18. Februar 2009, 11:28
von helduel
Moin,

da kw ein dict ist, kannst du mit kw.get('active') darauf zugreifen. get liefert dir None zurück, wenn es 'active' nicht gibt. Allerdings solltest du die Sternchen-Argumente nur dann verwenden, wenn sie wirklich sinnvoll sind. Wenn du eh weißt, dass ein Argument optional ist und 'active' heißen soll, dann ist das besser:

Code: Alles auswählen

def addUser(username, password, active=None):
   ...
Wenn's natürlich irgendwann zu unübersichtlich wird, weil einfach zuviele optionale Argumente da sind, dann kann ein Sternchen-Argument für sauberen Code sorgen. Sinnvoll sind sie auch, wenn die Anzahl der Argumente tatsächlich beliebig sein kann, z.B. wenn deine Funktion mit beliebigen Key-Value-Paaren etwas anstellt (wie bei dict).

Gruß,
Manuel

Verfasst: Mittwoch 18. Februar 2009, 11:39
von CM
Hoi,

manchmal sagt ein Beispiel mehr als tausend Wort:

Code: Alles auswählen

def foo(arg, *args, **kwds):
    print "arg = ", arg
    print "args = ", args
    print "kwds = ", kwds

foo("a", 1, ('b', 'c'), active = "parameter")
ergibt:

Code: Alles auswählen

arg =  a
args =  (1, ('b', 'c'))
kwds =  {'active': 'parameter'}
Jetzt weiss ich na nicht, was Du falsch gemacht hast, aber wenn Du den Paramter 'active' so übergibst:

Code: Alles auswählen

addUser("name", "passwort", 'active')
bekommst Du eine Fehlermeldung, weil 'active' nun mal ein Argument ist, das ein Schlüsselwort darstellt, also, wie oben, eine Zuweisung erfordert.

Zu Deiner zweiten Frage:
In einem solchen Fall kannst Du auf Defaults zurückgreifen. Z. B. so

Code: Alles auswählen

def foo('active' = 'ja'):
oder so

Code: Alles auswählen

def foo(**kwds):
Im zweiten Fall mußt Du abfragen, ob ein Schlüssel übergeben wurde:

Code: Alles auswählen

kwds.has_key('active')
und entsprechend reagieren. Option 2 würde ich nehmen, wenn es viele mögliche Argumente geben soll (mache ich sehr, sehr selten).

Lass mal sehen, wie Du das anpacken würdest - dann kann man konkrete Tipps geben.

Gruß,
Christian

Verfasst: Mittwoch 18. Februar 2009, 13:01
von Leonidas
Der Großteil des Codes nennt die Parameter ``args`` und ``kwargs`` - daher ist es oftmals sinnvoll das so beizubehalten. Dann weiß man in einer Funktionssignatur ganz schnell was gemeint ist.

Verfasst: Mittwoch 18. Februar 2009, 15:41
von name
CM hat geschrieben:

Code: Alles auswählen

def foo('active' = 'ja'):
aehm? is sind da ned die ' zu viel? Also

Code: Alles auswählen

def foo(active='ja'):

Verfasst: Mittwoch 18. Februar 2009, 15:44
von CM
Ja. Danke.

Am besten ich höre für heute auf am Rechner zu sitzen - ich mache zu viele dumme Fehler ... ;-)

Verfasst: Sonntag 22. Februar 2009, 17:19
von nkoehring
Hi CM,

du koenntest auch *args verwenden um sowas zu erreichen:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

def adduser(name, passwd, *args):
    # ...do something with name
    # ...do something with passwd
    if "active" in args:
        # ...thats an active user...
Benutzt wird das dann wie erwartet so:

Code: Alles auswählen

# aktiver user:
adduser("foo", "bar", "active")
# nicht aktiv:
adduser("foo", "bar")

Verfasst: Sonntag 22. Februar 2009, 17:26
von DasIch
Es geht übrigens auch folgendes:

Code: Alles auswählen

def foo(spam, eggs):
    pass

foo(**{'spam': 'bar', 'eggs': 'baz'})
Das hat allerdings zur Folge dass...

Code: Alles auswählen

foo('something', **{'spam': 'bar', 'eggs': 'baz'})
... zu einem TypeError führt.