E731 weist keinen Lambda-Ausdruck zu, verwendet ein def


193

Ich erhalte diese pep8-Warnung, wenn ich Lambda-Ausdrücke verwende. Werden Lambda-Ausdrücke nicht empfohlen? Wenn nicht warum?


4
Aus Gründen der Klarheit bezieht sich die Frage auf eine Nachricht für ein automatisiertes Einchecken flake8( flake8.pycqa.org )
Rakslice

Antworten:


229

Die Empfehlung in PEP-8, auf die Sie stoßen, lautet:

Verwenden Sie immer eine def-Anweisung anstelle einer Zuweisungsanweisung, die einen Lambda-Ausdruck direkt an einen Namen bindet.

Ja:

def f(x): return 2*x 

Nein:

f = lambda x: 2*x 

Die erste Form bedeutet, dass der Name des resultierenden Funktionsobjekts spezifisch 'f' anstelle des generischen '<lambda>' ist. Dies ist im Allgemeinen für Tracebacks und Zeichenfolgendarstellungen nützlicher. Die Verwendung der Zuweisungsanweisung eliminiert den einzigen Vorteil, den ein Lambda-Ausdruck gegenüber einer expliziten def-Anweisung bieten kann (dh, dass er in einen größeren Ausdruck eingebettet werden kann).

Das Zuweisen von Lambdas zu Namen dupliziert im Grunde nur die Funktionalität von def- und im Allgemeinen ist es am besten, etwas auf eine einzige Weise zu tun, um Verwirrung zu vermeiden und die Klarheit zu erhöhen.

Der legitime Anwendungsfall für Lambda ist, wenn Sie eine Funktion verwenden möchten, ohne sie zuzuweisen, z.

sorted(players, key=lambda player: player.rank)

Im Allgemeinen ist das Hauptargument dagegen, dass defAnweisungen zu mehr Codezeilen führen. Meine Hauptantwort darauf wäre: Ja, und das ist in Ordnung. Wenn Sie nicht Code-Golf spielen, sollten Sie die Anzahl der Linien nicht minimieren: Gehen Sie klar über kurz.


5
Ich sehe nicht, wie es schlimmer ist. Der Traceback wird weiterhin die fehlerhafte Zeilennummer und die Quelldatei enthalten. Man könnte "f" sagen, während der andere "Lambda" sagt. Vielleicht ist der Lambda-Fehler einfacher zu scannen, weil es sich nicht um einen einstelligen Funktionsnamen oder einen schlecht benannten langen Namen handelt?
g33kz0r

4
@ g33kz0r Nun, sicher, wenn Sie davon ausgehen, dass der Rest Ihres Codes eine schlechte Qualität haben wird, werden Sie durch Befolgen von Konventionen nicht viel gewinnen. Im Allgemeinen ist es nicht das Ende der Welt, aber es ist immer noch eine schlechte Idee.
Gareth Latty

39
Diese Antwort ist nicht sehr hilfreich, denn wenn Sie den vorgeschlagenen Ansatz der Verwendung defdurch den PEP8-Checker ausführen, erhalten Sie E704 multiple statements on one line (def), und wenn Sie ihn in zwei Zeilen aufteilen, erhalten Sie E301 expected 1 blank line, found 0: - /
Adam Spiers

4
Ich bin damit einverstanden, dass es aufgeteilt wird. Meine Punkte waren, dass a) es nicht im obigen Code der Antwort geteilt ist, was E704 verursacht, und b) wenn Sie es teilen, brauchen Sie eine hässliche leere Zeile darüber, um E301 zu vermeiden.
Adam Spiers

3
Ich benutze Lambdas, wenn ich eine reine Funktion hervorheben möchte (keine Nebenwirkungen), und manchmal muss ich dieselbe Funktion an zwei Stellen verwenden, dh gruppieren und zusammen sortieren. Also ignoriere ich diese Konvention.
Manu

119

Hier ist die Geschichte, ich hatte eine einfache Lambda-Funktion, die ich zweimal benutzte.

a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)

Dies ist nur für die Darstellung, ich habe einige verschiedene Versionen davon konfrontiert.

Um die Dinge trocken zu halten, beginne ich, dieses gewöhnliche Lambda wiederzuverwenden.

f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

An diesem Punkt beschwert sich mein Codequalitätsprüfer darüber, dass Lambda eine benannte Funktion ist, also konvertiere ich es in eine Funktion.

def f(x):
    return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

Jetzt beschwert sich der Prüfer, dass eine Funktion vorher und nachher durch eine Leerzeile begrenzt werden muss.

def f(x):
    return x + offset

a = map(f, simple_list)
b = map(f, another_simple_list)

Hier haben wir jetzt 6 Codezeilen anstelle der ursprünglichen 2 Zeilen ohne Erhöhung der Lesbarkeit und ohne Erhöhung der Python. Zu diesem Zeitpunkt beschwert sich die Codeprüfung über die Funktion ohne Dokumentzeichenfolgen.

Meiner Meinung nach sollte diese Regel besser vermieden und gebrochen werden, wenn es Sinn macht. Verwenden Sie Ihr Urteilsvermögen.


13
a = [x + offset for x in simple_list]. Keine Notwendigkeit zu verwenden mapund lambdahier.
Georgy

8
@Georgy Ich glaube, es ging darum, den x + offsetTeil an einen abstrahierten Ort zu verschieben, der aktualisiert werden kann, ohne mehr als eine Codezeile zu ändern. Bei Listenverständnissen, wie Sie bereits erwähnt haben, benötigen Sie noch zwei Codezeilen, die enthalten sind, dass x + offsetsie sich gerade in Listenverständnissen befinden. Um diese nach Wunsch des Autors herauszuziehen, benötigen Sie ein defoder lambda.
Julian

1
@Julian Abgesehen von defund lambdaman könnte auch functools.partial verwenden : f = partial(operator.add, offset)und dann a = list(map(f, simple_list)).
Georgy

Was ist mit def f(x): return x + offset(dh einer einfachen Funktion, die in einer einzelnen Zeile definiert ist)? Zumindest mit flake8 bekomme ich keine Beschwerden über Leerzeilen.
DocOc

1
@ Julian In einigen Fällen können Sie ein verschachteltes Verständnis verwenden:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea

24

Lattyware ist absolut richtig: Grundsätzlich möchte PEP-8, dass Sie Dinge wie vermeiden

f = lambda x: 2 * x

und stattdessen verwenden

def f(x):
    return 2 * x

Wie in einem kürzlich veröffentlichten Bugreport (August 2014) angesprochen , sind Aussagen wie die folgenden nun konform:

a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x

Da mein PEP-8-Checker dies noch nicht korrekt implementiert, habe ich E731 vorerst ausgeschaltet.


8
Selbst bei Verwendung defbeschwert sich der PEP8-Checker bei E301 expected 1 blank line, found 0, sodass Sie davor eine hässliche Leerzeile einfügen müssen.
Adam Spires

1

Ich bin auch auf eine Situation gestoßen, in der es sogar unmöglich war, eine def (ined) -Funktion zu verwenden.

class SomeClass(object):
  # pep-8 does not allow this
  f = lambda x: x + 1  # NOQA

  def not_reachable(self, x):
    return x + 1

  @staticmethod
  def also_not_reachable(x):
    return x + 1

  @classmethod
  def also_not_reachable(cls, x):
    return x + 1

  some_mapping = {
      'object1': {'name': "Object 1", 'func': f},
      'object2': {'name': "Object 2", 'func': some_other_func},
  }

In diesem Fall wollte ich unbedingt ein Mapping erstellen, das zur Klasse gehört. Einige Objekte in der Zuordnung benötigten dieselbe Funktion. Es wäre unlogisch, die benannte Funktion außerhalb der Klasse zu platzieren. Ich habe keine Möglichkeit gefunden, innerhalb des Klassenkörpers auf eine Methode (statische Methode, Klassenmethode oder normal) zu verweisen. SomeClass existiert noch nicht, wenn der Code ausgeführt wird. Es ist also auch nicht möglich, sich aus der Klasse darauf zu beziehen.


Sie könnten also_not_reachablein der Mapping-Definition alsSomeClass.also_not_reachable
yaccz

1
Ich weiß nicht, welchen Punkt Sie hier ansprechen wollen. Jeder Ihrer Funktionsnamen ist ffür mich so erreichbar wie in 2.7 und 3.5
Eric

Nein, alle Funktionen außer der Lambda-Funktion sind nicht innerhalb des Klassenkörpers erreichbar. Sie erhalten einen AttributeError: Typ Objekt 'SomeClass' hat kein Attribut '...', wenn Sie versuchen, auf eine dieser Funktionen im some_mapping-Objekt zuzugreifen.
simP

3
@simP alle sind perfekt zugänglich. Diejenigen mit @staticmethodund @classmethodohne Objekt, nur SomeClass.also_not_reachable(obwohl sie eindeutige Namen benötigen). Wenn Sie über Klassenmethoden auf sie zugreifen müssen, verwenden Sie einfachself.also_not_reachable
ababak

@simP Vielleicht solltest du deine *not_reachableMethoden in not_as_easily_reachable_from_class_definition_as_a_lambdaxD umbenennen
Romain Vincent
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.