Python *args, **keywords

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
danims
User
Beiträge: 29
Registriert: Montag 19. Februar 2007, 20:23
Wohnort: Bern, Schweiz

Mittwoch 18. Februar 2009, 11:06

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"?
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Mittwoch 18. Februar 2009, 11:28

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
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Mittwoch 18. Februar 2009, 11:39

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
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 18. Februar 2009, 13:01

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.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Mittwoch 18. Februar 2009, 15:41

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'):
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Mittwoch 18. Februar 2009, 15:44

Ja. Danke.

Am besten ich höre für heute auf am Rechner zu sitzen - ich mache zu viele dumme Fehler ... ;-)
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

Sonntag 22. Februar 2009, 17:19

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")
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
DasIch
User
Beiträge: 2452
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sonntag 22. Februar 2009, 17:26

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.
Antworten