E731 non assegna un'espressione lambda, usa un def


193

Ricevo questo avviso pep8 ogni volta che uso espressioni lambda. Le espressioni lambda non sono consigliate? Se no perché?


4
Per chiarezza, la domanda si riferisce a un messaggio per un check-in automatico flake8( flake8.pycqa.org )
rakslice

Risposte:


232

La raccomandazione in PEP-8 in cui ti imbatti è:

Utilizzare sempre un'istruzione def anziché un'istruzione di assegnazione che associa un'espressione lambda direttamente a un nome.

Sì:

def f(x): return 2*x 

No:

f = lambda x: 2*x 

La prima forma indica che il nome dell'oggetto funzione risultante è specificamente 'f' invece del generico '<lambda>'. Questo è più utile per traceback e rappresentazioni di stringhe in generale. L'uso dell'istruzione di assegnazione elimina l'unico vantaggio che un'espressione lambda può offrire rispetto a un'istruzione esplicita def (ovvero che può essere incorporata in un'espressione più grande)

Assegnare lambdas ai nomi sostanzialmente duplica la funzionalità di def- e in generale, è meglio fare qualcosa in un solo modo per evitare confusione e aumentare la chiarezza.

Il caso d'uso legittimo per lambda è dove si desidera utilizzare una funzione senza assegnarla, ad esempio:

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

In generale, l'argomento principale contro questo è che le defistruzioni comporteranno più righe di codice. La mia risposta principale sarebbe: sì, e va bene. A meno che tu non stia giocando a golf, ridurre al minimo il numero di linee non è qualcosa che dovresti fare: cerca di abbreviare in breve.


5
Non vedo come sia peggio. Il traceback includerà ancora il numero di riga errante e il file di origine. Uno potrebbe dire "f" mentre l'altro dice "lambda". Forse l'errore lambda è più facile da scansionare perché non è un nome di funzione a carattere singolo o un nome lungo mal chiamato?
g33kz0r,

4
@ g33kz0r Beh, certo, se pensi che il resto del tuo codice avrà una qualità scadente, seguire le convenzioni non ti farà guadagnare molto. In generale, no, non è la fine del mondo, ma è ancora una cattiva idea.
Gareth Latty,

40
Questa risposta non è molto utile, perché quando si esegue l'approccio suggerito di utilizzare deftramite il correttore PEP8, si ottiene E704 multiple statements on one line (def)e se si divide in due righe si ottiene E301 expected 1 blank line, found 0: - /
Adam Spires

4
Sono d'accordo che dovrebbe essere diviso. I miei punti erano che a) non è diviso nel codice della risposta sopra, causando E704 eb) se lo dividi, hai bisogno di una brutta linea bianca sopra per evitare E301.
Adam Spires,

3
Uso lambdas quando voglio enfatizzare una funzione pura (senza effetti collaterali), e talvolta devo usare la stessa funzione in due posti, cioè groupby e ordinarli insieme. Quindi ignoro questa convenzione.
manu,

119

Ecco la storia, ho avuto una semplice funzione lambda che stavo usando due volte.

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

Questo è solo per la rappresentazione, ho affrontato un paio di versioni diverse di questo.

Ora, per mantenere le cose ASCIUTTE, inizio a riutilizzare questa lambda comune.

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

A questo punto il mio controllo qualità codice si lamenta del fatto che lambda sia una funzione con nome, quindi la converto in una funzione.

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

Ora il controllore si lamenta che una funzione deve essere delimitata da una riga vuota prima e dopo.

def f(x):
    return x + offset

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

Qui abbiamo ora 6 righe di codice invece di 2 righe originali senza aumento della leggibilità e nessun aumento nell'essere pitone. A questo punto il correttore di codice si lamenta della funzione che non ha docstring.

Secondo me è meglio evitare e infrangere questa regola quando ha senso, usa il tuo giudizio.


13
a = [x + offset for x in simple_list]. Non c'è bisogno di usare mape lambdaqui.
Georgy,

8
@Georgy Credo che il punto fosse spostare la x + offsetporzione in una posizione astratta che può essere aggiornata senza cambiare più di una riga di codice. Con la comprensione degli elenchi, come hai menzionato, avresti comunque bisogno di due righe di codice che contenessero, x + offsetche ora sarebbero comprese negli elenchi. Per estrarre quelli come voleva l'autore, avresti bisogno di un defo lambda.
Julian,

1
@Julian Oltre a defe lambdasi potrebbe anche usare functools.partial : f = partial(operator.add, offset)e poi a = list(map(f, simple_list)).
Georgy,

Che dire di def f(x): return x + offset(cioè una semplice funzione definita su una sola riga)? Almeno con flake8 non ricevo lamentele per le righe vuote.
DocOc,

1
@Julian In alcuni casi puoi usare una comprensione annidata:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea

24

Lattyware ha assolutamente ragione: in pratica PEP-8 vuole che tu eviti cose del genere

f = lambda x: 2 * x

e invece usa

def f(x):
    return 2 * x

Tuttavia, come indicato in un recente report di bug (agosto 2014), dichiarazioni come le seguenti sono ora conformi:

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

Poiché il mio correttore PEP-8 non lo implementa ancora correttamente, per il momento ho disattivato E731.


8
Anche durante l'utilizzo def, il correttore PEP8 si lamenta E301 expected 1 blank line, found 0, quindi devi aggiungere una brutta riga vuota prima di esso.
Adam Spires,

1

Ho anche riscontrato una situazione in cui era persino impossibile usare una funzione def (ined).

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 questo caso, volevo davvero fare una mappatura appartenente alla classe. Alcuni oggetti nella mappatura necessitavano della stessa funzione. Sarebbe illogico mettere la funzione a al di fuori della classe. Non ho trovato il modo di fare riferimento a un metodo (metodo statico, metodo di classe o normale) all'interno del corpo della classe. SomeClass non esiste ancora quando viene eseguito il codice. Quindi non è nemmeno possibile fare riferimento ad esso dalla classe.


È possibile fare riferimento also_not_reachablenella definizione della mappatura comeSomeClass.also_not_reachable
yaccz,

1
Non so che punto stai cercando di chiarire qui. Tutti i nomi delle tue funzioni sono raggiungibili come fin 2.7 e 3.5 per me
Eric,

No, tutte le funzioni, ad eccezione della funzione lambda, non sono raggiungibili all'interno del corpo della classe. Otterrai un AttributeError: l'oggetto tipo 'SomeClass' non ha attributi '...' se provi ad accedere a una di quelle funzioni nell'oggetto some_mapping.
simP

3
@simP sono tutti perfettamente accessibili. Quelli con @staticmethode @classmethodnon hanno bisogno di un oggetto, solo SomeClass.also_not_reachable(anche se hanno bisogno di nomi distintivi). Se devi accedervi dai metodi di classe, usa semplicementeself.also_not_reachable
ababak

@simP forse dovresti rinominare i tuoi *not_reachablemetodi come not_as_easily_reachable_from_class_definition_as_a_lambdaxD
Romain Vincent il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.