it-swarm.dev

Warum erlaubt Python nicht mehrzeilige Lambdas?

Kann jemand die konkreten Gründe erklären, warum BDFL sich dafür entschieden hat, Python lambdas single line) zu machen?

Das ist gut:

lambda x: x**x

Dies führt zu einem Fehler:

lambda x:
    x**x

Ich verstehe, dass die Mehrzeiligkeit von Lambda die normalen Einrückungsregeln irgendwie "stören" würde und das Hinzufügen weiterer Ausnahmen erfordern würde, aber ist das nicht die Vorteile wert?

Schauen Sie sich zum Beispiel JavaScript an. Wie kann man ohne diese anonymen Funktionen leben? Sie sind unverzichtbar. Wollen Pythonisten nicht loswerden, dass sie jede mehrzeilige Funktion benennen müssen, um sie als Argument zu übergeben?

53
treecoder

Guido van van Rossum antwortete selbst:

Bei solchen Lösungen fehlt es jedoch häufig an "Pythonicity" - dem schwer fassbaren Merkmal einer guten Python - Funktion. Es ist unmöglich, Pythonizität als harte Einschränkung auszudrücken. Selbst das Zen von Python lässt sich nicht in einen einfachen Test der Pythonizität übersetzen ...

Im obigen Beispiel ist die Achillesferse der vorgeschlagenen Lösung leicht zu finden: Der Doppelpunkt ist zwar syntaktisch eindeutig (eine der "Puzzle-Einschränkungen"), aber völlig willkürlich und ähnelt nichts anderem in Python ...

Aber ich lehne das auch ab, weil ich am Ende (und hier gebe ich zu, den Einsender unbeabsichtigt irrezuführen) jede Lösung inakzeptabel finde, die einen auf Einrückungen basierenden Block in die Mitte eines Ausdrucks einbettet. Da ich alternative Syntax für die Gruppierung von Anweisungen (z. B. geschweifte Klammern oder Schlüsselwörter für Anfang/Ende) gleichermaßen inakzeptabel finde, macht dies ein mehrzeiliges Lambda zu einem unlösbaren Rätsel.

http://www.artima.com/weblogs/viewpost.jsp?thread=147358

Grundsätzlich sagt er, dass eine Lösung zwar möglich ist, aber nicht mit Python übereinstimmt.

46
BlackJack

es ist vollkommen in Ordnung, ein mehrzeiliges Lambda in Python zu machen: siehe

>>> f = lambda x: (
...   x**x)
>>> f
<function <lambda> at 0x7f95d8f85488>
>>> f(3)
27

die wahre Lambda-Einschränkung ist die Tatsache, dass Lambda ein einzelnes Ausdruck sein muss. Es kann kein Schlüsselwort enthalten (wie das print oder return von python2).

GvR wählt dies, um die Größe des Lambda zu begrenzen, da diese normalerweise als Parameter verwendet werden. Wenn Sie eine echte Funktion wünschen, verwenden Sie def

25
Vito De Tullio

Ich weiß, das ist super alt, aber hier als Referenz.

Eine Alternative zur Verwendung von Lambda könnte darin bestehen, ein def auf nicht konventionelle Weise zu verwenden. Das Ziel ist es, ein def an eine Funktion zu übergeben, was nur unter einem Umstand möglich ist - einem Dekorateur. Beachten Sie, dass mit dieser Implementierung def result Keine Funktion erstellt wird, sondern das Ergebnis von reduce(), das letztendlich ein dict ist.

Schamloser Stecker : Ich mache viel davon hier .

>>> xs = [('a', 1), ('b', 2), ('a', 3), ('b', 4)]
>>> foldl = lambda xs, initial: lambda f: reduce(f, xs, initial)
>>> @foldl(xs, {})
... def result(acc, (k, v)):
...     acc.setdefault(k, 0)
...     acc[k] += v
...     return acc
...
>>> result
{'a': 4, 'b': 6} 

Beachten Sie, dass Lambdas mit mehreren Anweisungen können ausgeführt werden können, jedoch nur mit wirklich, wirklich hässlichem Code. Interessant ist jedoch, wie das Scoping mit dieser Implementierung funktioniert (beachten Sie die mehrfache Verwendung der Variablen name und das Abschatten der Variablen message.

>>> from __future__ import print_function
>>> bind = lambda x, f=(lambda x: x): f(x)
>>> main = lambda: bind(
...     print('Enter your name.'), lambda _: bind(
...     raw_input('> '), lambda name: bind(
...     'Hello {}!'.format(name), lambda message: bind(
...     print(message), lambda _: bind(
...     'Bye {}!'.format(name), lambda message: bind(
...     print(message)
... ))))))
>>> main()
Enter your name.
> foo
Hello foo!
Bye foo!
10
pyrospade

Ein Lambda mit mehreren Anweisungen zusammen zu hacken ist nicht ganz so schlimm, wie Pyrospade es ausmacht: Sicher, dass wir könnten eine Reihe monadischer Funktionen mit bind zusammenstellen, wie in Haskell, aber da wir in der In der unreinen Welt von Python könnten wir genauso gut Nebenwirkungen verwenden, um dasselbe zu erreichen.

Ich beschreibe ein paar Möglichkeiten, dies auf meinem Blog zu tun.

Zum Beispiel garantiert Python garantiert, dass die Elemente eines Tupels der Reihe nach ausgewertet werden, sodass wir , Ähnlich wie einen Imperativ ; Verwenden können. Wir können viele Anweisungen ersetzen , wie print, mit Ausdrücken wie sys.stdout.write.

Daher sind die folgenden äquivalent:

def print_in_tag_def(tag, text):
    print "<" + tag + ">"
    print text
    print "</" + tag + ">"

import sys
print_ = sys.stdout.write
print_in_tag_lambda = lambda tag, text: (print_("<" + tag + ">"),
                                         print_(text),
                                         print_("</" + tag + ">"),
                                         None)[-1]

Beachten Sie, dass ich am Ende ein None hinzugefügt und es mit [-1] Extrahiert habe. Dadurch wird der Rückgabewert explizit festgelegt. Wir müssen das nicht tun, aber ohne es würden wir einen funky (None, None, None) Rückgabewert erhalten, den wir vielleicht interessieren oder nicht.

Wir können also IO Aktionen) sequenzieren. Was ist mit lokalen Variablen?

Pythons = Bildet eine Anweisung, daher müssen wir einen äquivalenten Ausdruck finden. Eine Möglichkeit besteht darin, den Inhalt der Datenstruktur zu mutieren, der als Argument übergeben wird. Zum Beispiel:

def stateful_def():
    foo = 10
    bar = foo * foo
    foo = 2
    return foo + bar

stateful_lambda = (lambda state: lambda *_: (state.setdefault('foo', 10),
                                             state.setdefault('bar', state.get('foo') * state.get('foo')),
                                             state.pop('foo'),
                                             state.setdefault('foo', 2),
                                             state.get('foo') + state.get('bar'))[-1])({})

In stateful_lambda Werden nur wenige Tricks verwendet:

  • Das Argument *_ Ermöglicht unserem Lambda, any Anzahl der Argumente anzunehmen. Da dies Null Argumente zulässt, stellen wir die Aufrufkonvention von stateful_def Wieder her.
    • Das Aufrufen eines Arguments _ Ist nur eine Konvention, die besagt: "Ich werde diese Variable nicht verwenden."
  • Wir haben eine ("Wrapper") Funktion, die eine andere ("Haupt") Funktion zurückgibt: lambda state: lambda *_: ...
    • Dank lexikalischer Bereich ist das Argument der ersten Funktion für die zweite Funktion in-scope
    • Das Akzeptieren einiger Argumente und das Zurückgeben einer anderen Funktion, um den Rest später zu akzeptieren, wird als Currying bezeichnet
  • Wir rufen sofort die "Wrapper" -Funktion auf und übergeben ihr ein leeres Wörterbuch: (lambda state: ...)({})
    • Auf diese Weise können wir einem Wert {} Eine Variable state zuweisen, ohne eine Zuweisungsanweisung zu verwenden (z. B. state = {}).
  • Wir behandeln Schlüssel und Werte in state als Variablennamen und gebundene Werte
    • Dies ist weniger umständlich als die Verwendung von sofort genannten Lambdas
    • Dies ermöglicht es uns, die Werte von Variablen zu mutieren
    • Wir verwenden state.setdefault(a, b) anstelle von a = b Und state.get(a) anstelle von a
  • Wir verwenden ein Tupel, um unsere Nebenwirkungen wie zuvor miteinander zu verketten
  • Wir verwenden [-1], Um den letzten Wert zu extrahieren, der sich wie eine return -Anweisung verhält

Natürlich ist dies ziemlich umständlich, aber wir können eine schönere API mit Hilfsfunktionen erstellen:

# Keeps arguments and values close together for immediately-called functions
callWith = lambda x, f: f(x)

# Returns the `get` and `setdefault` methods of a new dictionary
mkEnv = lambda *_: callWith({},
                            lambda d: (d.get,
                                       lambda k, v: (d.pop(k), d.setdefault(k, v))))

# A helper for providing a function with a fresh `get` and `setdefault`
inEnv = lambda f: callWith(mkEnv(), f)

# Delays the execution of a function
delay = lambda f x: lambda *_: f(x)

# Uses `get` and `set`(default) to mutate values
stateful_lambda = delay(inEnv, lambda get, set: (set('foo', 10),
                                                 set('bar', get('foo') * get('foo')),
                                                 set('foo', 2),
                                                 get('foo') + get('bar'))[-1])
4
Warbo

Ich dachte, ich könnte dazu beitragen, einen Zeilenumbruch zu verwenden:

x = lambda x,y: x-y if x<y \ 
                     else y-x if y<x \
                     else 0

Vergessen Sie nicht die sehr schöne Sache, dass python Oneliners schreiben kann, wie im Beispiel:

a=b=0; c=b+a; d = a+b**2 #etc etc

Und das Lambda ist sehr mächtig, aber es ist nicht zum Ersetzen einer ganzen Funktion gedacht, ich meine, Sie könnten es so hacken (Beispiel von einem Kollegen oben ausleihen):

makeTag = lambda tagName: "<{}>".format(tagName)
closeTag = lambda tagName: makeTag("/"+str(tagName))
openTag = lambda tagName: makeTag(tagName)
writeHMTLline = lambda tag,content: ""+opetTag(tag)+str(content)+closeTag(tag)

Aber willst du das wirklich so machen? Es ist nach einiger Zeit meistens unlesbar, es ist, als würde man an den Anfang des Seils gelangen, beginnend mit dem enträtselten Ende. (unraveled rope

Lambdas werden als One-Only-Funktionen in Map, Filter und Reduce-Funktionen in der funktional orientierten Programmierung (unter anderem) erwähnt. Zum Beispiel Zeichenwerte von Werten abrufen, die ganzzahlig und durch 2 teilbar sind

chrDev2 = lambda INT: chr(INT) if isinstance(INT,int) and INT%2==0 else INT
someStringList = map( chrDev2, range(30) )
>>> ['\x00', 1, '\x02', 3, '\x04', 5, '\x06', 7, '\x08', 9, '\n', 11, '\x0c', 13, '\x0e', 15, '\x10', 17, '\x12', 19, '\x14', 21, '\x16', 23, '\x18', 25, '\x1a', 27, '\x1c', 29]

Sie können es als Funktionsausdrucksfunktion verwenden, indem Sie komplexe Funktionen (oder mehrere Lambdas) definieren und in ein anderes Lambda einfügen:

def someAnon(*args): return sum(list(args))
defAnon = lambda list: [ x*someAnon(*list) for x in list]

aber Python unterstützt Funktionsausdrücke auf andere Weise: -lets sagen, Sie haben eine Funktion namens superAwesomeFunction und diese Funktion kann einige super tolle Dinge tun, Sie können sie einer Variablen zuweisen indem du es nicht so nennst:

SAF = superAwesomeFunction # there is no () at the end, 

Wenn Sie jetzt SAF aufrufen, rufen Sie superAwesomeFunction oder method auf. Wenn Sie in Ihrem Lib-Ordner suchen, können Sie feststellen, dass die meisten von python __builtin__ Module sind so geschrieben. Dies geschieht, weil Sie manchmal einige Funktionen benötigen, die bestimmte Aufgaben ausführen, die nicht erforderlich genug sind, um vom Benutzer verwendet werden zu können, aber für mehrere Funktionen erforderlich sind. Dann haben Sie die Wahl, Sie können nicht 2 Funktionen mit dem Namen "superAwesomeFunction" haben, Sie können "superAwesomeFunctionDoingBasicStuf" und "realSuperAwesomeFunction" haben und dann einfach die "realSuperAwesomeFunction" in die Variable "superAwesomeFunction" setzen und Sie sind fertig.

Den Speicherort der importierten Module finden Sie in der Konsole importedModule.__file__ (echtes Beispiel import os;os.__file__), und folgen Sie einfach diesem Verzeichnis zu der Datei importiertModule.py und öffnen Sie sie im Editor und finden Sie heraus, wie Sie Ihr eigenes "Wissen" maximieren können.

Ich hoffe, das hilft Ihnen und vielleicht anderen Kollegen in Schwierigkeiten.

1
Danilo