Ricevo questo avviso pep8 ogni volta che uso espressioni lambda. Le espressioni lambda non sono consigliate? Se no perché?
Ricevo questo avviso pep8 ogni volta che uso espressioni lambda. Le espressioni lambda non sono consigliate? Se no perché?
Risposte:
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 def
istruzioni 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.
def
tramite 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
: - /
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.
a = [x + offset for x in simple_list]
. Non c'è bisogno di usare map
e lambda
qui.
x + offset
porzione 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 + offset
che ora sarebbero comprese negli elenchi. Per estrarre quelli come voleva l'autore, avresti bisogno di un def
o lambda
.
def
e lambda
si potrebbe anche usare functools.partial : f = partial(operator.add, offset)
e poi a = list(map(f, simple_list))
.
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.
a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
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.
def
, il correttore PEP8 si lamenta E301 expected 1 blank line, found 0
, quindi devi aggiungere una brutta riga vuota prima di esso.
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.
also_not_reachable
nella definizione della mappatura comeSomeClass.also_not_reachable
f
in 2.7 e 3.5 per me
@staticmethod
e @classmethod
non 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
*not_reachable
metodi come not_as_easily_reachable_from_class_definition_as_a_lambda
xD
flake8
( flake8.pycqa.org )