Auf codewars.com bin ich auf folgende Aufgabe gestoßen:
Erstellen Sie eine Funktion
add
, die beim aufeinanderfolgenden Aufruf Zahlen addiert. Alsoadd(1)
sollte zurückkehren1
,add(1)(2)
sollte zurückkehren1+2
, ...
Obwohl ich mit den Grundlagen von Python vertraut bin, bin ich noch nie auf eine Funktion gestoßen, die in einer solchen Folge f(x)
aufgerufen werden kann , dh eine Funktion , die als aufgerufen werden kann f(x)(y)(z)...
. Bisher bin ich mir nicht einmal sicher, wie ich diese Notation interpretieren soll.
Als Mathematiker würde ich vermuten, dass dies f(x)(y)
eine Funktion ist, die jeder x
Funktion eine Funktion zuweist g_{x}
und dann zurückkehrt g_{x}(y)
und ebenfalls für f(x)(y)(z)
.
Sollte diese Interpretation korrekt sein, würde Python mir erlauben, dynamisch Funktionen zu erstellen, was mir sehr interessant erscheint. Ich habe in der letzten Stunde im Internet gesucht, konnte aber keinen Hinweis in die richtige Richtung finden. Da ich jedoch nicht weiß, wie dieses Programmierkonzept heißt, ist dies möglicherweise nicht allzu überraschend.
Wie nennt man dieses Konzept und wo kann ich mehr darüber lesen?
quelle
functools.partial()
| WP: ClosuresAntworten:
Ich weiß nicht, ob dies eine Funktionsverkettung ist , sondern eine aufrufbare Verkettung, aber da Funktionen aufrufbar sind , wird wohl kein Schaden angerichtet. In beiden Fällen kann ich mir zwei Möglichkeiten vorstellen:
Unterklassifizierung
int
und Definition__call__
:Der erste Weg wäre mit einer benutzerdefinierten
int
Unterklasse, die definiert,__call__
welche eine neue Instanz von sich selbst mit dem aktualisierten Wert zurückgibt:Die Funktion
add
kann jetzt so definiert werden, dass eineCustomInt
Instanz zurückgegeben wird, die als aufrufbare Instanz, die einen aktualisierten Wert von sich selbst zurückgibt, nacheinander aufgerufen werden kann:Darüber hinaus als
int
Unterklasse, behält der zurückgegebene Wert das__repr__
und__str__
Verhaltenint
s. Für komplexere Vorgänge sollten Sie jedoch andere Dunder entsprechend definieren .Wie @Caridorc in einem Kommentar feststellte,
add
könnte man auch einfach schreiben als:Das Umbenennen der Klasse in
add
stattCustomInt
funktioniert ebenfalls ähnlich.Definieren Sie einen Abschluss, erfordert einen zusätzlichen Aufruf, um einen Wert zu erzielen:
Die einzige andere Möglichkeit, die ich mir vorstellen kann, ist eine verschachtelte Funktion, die einen zusätzlichen leeren Argumentaufruf erfordert, um das Ergebnis zurückzugeben. Ich verwende und entscheide mich nicht dafür,
nonlocal
Attribute an die Funktionsobjekte anzuhängen, um sie zwischen Pythons portierbar zu machen:Dies gibt kontinuierlich sich selbst zurück (
_inner_adder
), was, wenn a angegebenval
wird, es erhöht (_inner_adder += val
) und wenn nicht, den Wert so zurückgibt, wie er ist. Wie bereits erwähnt, ist ein zusätzlicher()
Aufruf erforderlich, um den inkrementierten Wert zurückzugeben:quelle
add = CostumInt
sollte auch funktionieren und einfacher sein.(2*add(1)(2))(3)
einTypeError
weil fehlschlägt, weilint
es nicht aufrufbar ist. GrundsätzlichCustomInt
wird das in normal konvertiert,int
wenn es in einem beliebigen Kontext verwendet wird, außer beim Aufrufen. Für eine robustere Lösung müssen Sie grundsätzlich alle__*__
Methoden einschließlich der__r*__
Versionen neu implementieren ...CustomInt
sondernadd
beim Definieren.Du kannst mich hassen, aber hier ist ein Einzeiler :)
Edit: Ok, wie geht das? Der Code ist identisch mit der Antwort von @Jim, aber alles geschieht in einer einzigen Zeile.
type
kann verwendet werden, um neue Typen zu erstellen :type(name, bases, dict) -> a new type
. Dennname
wir geben eine leere Zeichenfolge an, da der Name in diesem Fall nicht wirklich benötigt wird. Fürbases
(Tupel) stellen wir ein zur Verfügung(int,)
, das mit dem Erben identisch istint
.dict
sind die Klassenattribute, an die wir das__call__
Lambda anhängen .self.__class__(self + v)
ist identisch mitreturn CustomInt(self + v)
quelle
class add(int):__call__ = lambda self, v: add(self+v)
Wenn Sie eine Funktion definieren möchten, die mehrmals aufgerufen werden soll, müssen Sie zuerst jedes Mal ein aufrufbares Objekt zurückgeben (z. B. eine Funktion). Andernfalls müssen Sie Ihr eigenes Objekt erstellen, indem Sie ein
__call__
Attribut definieren , damit es aufrufbar ist.Der nächste Punkt ist, dass Sie alle Argumente beibehalten müssen. In diesem Fall möchten Sie möglicherweise Coroutines oder eine rekursive Funktion verwenden. Beachten Sie jedoch, dass Coroutinen speziell für solche Aufgaben viel optimierter / flexibler sind als rekursive Funktionen .
Hier ist eine Beispielfunktion mit Coroutines, die den neuesten Status von sich selbst beibehält. Beachten Sie, dass es nicht mehrmals aufgerufen werden kann, da der Rückgabewert
integer
nicht aufrufbar ist, aber Sie könnten darüber nachdenken, dies in Ihr erwartetes Objekt umzuwandeln ;-).quelle
Der pythonische Weg, dies zu tun, wäre die Verwendung dynamischer Argumente:
Dies ist nicht die Antwort, nach der Sie suchen, und Sie wissen das vielleicht, aber ich dachte, ich würde es trotzdem geben, denn wenn sich jemand wundert, dies nicht aus Neugier, sondern aus beruflichen Gründen zu tun. Sie sollten wahrscheinlich die "richtige Antwort" haben.
quelle
add = sum
wenn Sie diesen WegEinfach:
quelle
Wenn Sie bereit sind, eine zusätzliche zu akzeptieren
()
, um das Ergebnis abzurufen, können Sie Folgendes verwendenfunctools.partial
:Beispielsweise:
Auf diese Weise können auch mehrere Nummern gleichzeitig angegeben werden:
Wenn Sie es auf eine einzelne Zahl beschränken möchten, können Sie Folgendes tun:
Wenn Sie
add(x)(y)(z)
das Ergebnis sofort zurückgeben und weiter aufrufbar sein möchten ,int
ist die Unterklassifizierung der richtige Weg.quelle