Partial(): TypeError: multiple values for argument

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
Fenrix
User
Beiträge: 6
Registriert: Samstag 24. Oktober 2015, 21:47

Hey, ich versuche gerade mir Python beizubringen und beschäftige mich im Moment mit der partial Funktion.

Folgender Code funktioniert einwandfrei:

Code: Alles auswählen

from functools import partial


def f(x, y, z):
    print("x:", x, "y:", y, "z:", z)


f2 = partial(f, z=5, y=3)
f2(1)
Wenn ich bei der partial Funktion diese Tags nicht angebe werden die Parameter ja einfach von links nach rechts genommen. Aber in diesem Beispiel habe ich z und y gesetzt und somit ist praktisch nur x variabel in der neuen Funktion.

Sobald ich jetzt aber x mit diesem "Tag" setzen möchte, also:

Code: Alles auswählen

f2 = partial(f, x=5, z=3)
, dann kommt folgender Fehler:
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
TypeError: f() got multiple values for argument 'x'
Mir ist bewusst, dass ich genauso gut schreiben könnte:

Code: Alles auswählen

f2 = partial(f, 5, z=3)
allerdings verstehe ich nicht, warum ich bei dem ersten Parameter das Keyword nicht setzen kann, bei allen anderen aber schon.

Hoffe mir kann da jemand helfen

Grüße
BlackJack

@Fenrix: Wenn du bei `partial()` Schlüsselwort-Argumente angibt, bedeutet das nicht, dass die dann bei den positionalen Argumenten ausgespart werden. Wenn Du also bei `partial()` das `x`-Argument per Schlüssel setzt, dann hat das einmal deswegen einen Wert und dann weil beim Aufruf das erste Argument nochmal gesetzt wird, also zwei Argumente für `x`. Das geht halt nicht. Gehen würde: ``partial(f, x=5, z=3)(y=1)``.

Und das ist auch nicht auf das erste Argument beschränkt. Mit `y` kannst Du genau das gleiche Problem bekommen wenn Du nur ein Argument per `partial()` angibst:

Code: Alles auswählen

In [8]: partial(f, z=5)(1, 2)
('x:', 1, 'y:', 2, 'z:', 5)

In [9]: partial(f, y=5)(1, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-735442926fb7> in <module>()
----> 1 partial(f, y=5)(1, 2)

TypeError: f() got multiple values for keyword argument 'y'
Edit: In der Dokumentation ist eine Implementierung von `partial()` angegeben. Vielleicht hilft das ja beim Verständnis.
Fenrix
User
Beiträge: 6
Registriert: Samstag 24. Oktober 2015, 21:47

Danke, das macht Sinn.
Wobei mir jetzt auch bewusst wird, dass ich mir das mit den positionalen und Keyword-Argumenten nochmal genauer anschauen muss.

Wenn ich das richtig verstehe ist es also so: Angenommen ich habe eine Funktion mit 3 Parametern wie oben.

Wenn ich y nicht als Keyword in der neuen Funktion übergeben möchte sondern 'ganz normal' und x und z praktisch einfrieren will, dann ist die einzige Möglichkeit das zu machen so:

Code: Alles auswählen

f2 = partial(f, 1, z=3)
, also alles vor dem variablen Parameter positional und alles dahinter als Keyword.

Wenn ich jetzt eine Funktion habe:

Code: Alles auswählen

def test(a, b, c, d):
    print("a:", a, "b:", b, "c:", c, "d:", d)
Und ich möchte a und c "einfrieren" und b und d wiederum normal und nicht als Keyword übergeben, dann ist das unmöglich? Zumindest sollte es nach meinem Verständnis unmöglich sein, d positional zu übergeben.


Ich denke meine Verwirrung stammt daher, dass folgende Sachen nicht das gleiche sind:

Code: Alles auswählen

def f(x, y, z):
    print("x:", x, "y:", y, "z:", z)


f2 = partial(f, x=1, y=2)
f3 = partial(f, 1, 2)

f2(1)
f3(1)

Für mich etwas verwirrend, dass bei f2(1) die 1 nochmal als x Wert übergeben wird. Das mit den Keyword Argumenten muss ich mir nochmal genau angucken. Auf der anderen Seite ist ja ja egal ob ich f mit den positionalen oder Keyword Argumenten aufrufe:

Code: Alles auswählen

f(1, 2, 3)
f(z=3, y=2, x=1)
Am Anfang alles etwas verwirrend :?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das ist nicht nur für dich verwirrend. Prinzipiell könnte man partial auch so implementieren dass f2(1) 1 als z übergibt.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Randbemerkung:

Falls du Python 3 verwendest, könnte man überlegen, die Argumente von f als Keyword-Only zu spezifizieren. Dann muss y zwingend als Keyword übergeben werden; der Unterschied ist im Ergebnis halt, dass du dann 'ne andere Fehlermeldung bekommst, die aber vielleicht etwas schöner ist.

Code: Alles auswählen

In [1]: from functools import partial
In [2]: def f(x, y, z):
   ...:     return (x+y)*z
   ...: 

In [3]: f2 = partial(f, x=1, z=4)
In [4]: f2(5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-237c06c1abe6> in <module>()
----> 1 f2(5)

TypeError: f() got multiple values for argument 'x'

In [5]: f2(y=5)
Out[5]: 24
In [6]: def g(*, x, y, z):
    return (x+y)*z
   ...: 

In [7]: g2 = partial(g, x=1, z=4)
In [8]: g2(5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-c3efa9e21362> in <module>()
----> 1 g2(5)

TypeError: g() takes 0 positional arguments but 1 positional argument (and 2 keyword-only arguments) were given

In [9]: g2(y=5)
Out[9]: 24
Antworten