Seite 1 von 1

Partial(): TypeError: multiple values for argument

Verfasst: Sonntag 8. Mai 2016, 11:23
von Fenrix
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

Re: Partial(): TypeError: multiple values for argument

Verfasst: Sonntag 8. Mai 2016, 11:50
von 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.

Re: Partial(): TypeError: multiple values for argument

Verfasst: Sonntag 8. Mai 2016, 13:30
von Fenrix
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 :?

Re: Partial(): TypeError: multiple values for argument

Verfasst: Sonntag 8. Mai 2016, 13:59
von DasIch
Das ist nicht nur für dich verwirrend. Prinzipiell könnte man partial auch so implementieren dass f2(1) 1 als z übergibt.

Re: Partial(): TypeError: multiple values for argument

Verfasst: Sonntag 8. Mai 2016, 15:02
von nezzcarth
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