E731 n'attribue pas d'expression lambda, utilise une def
193
J'obtiens cet avertissement pep8 chaque fois que j'utilise des expressions lambda. Les expressions lambda ne sont-elles pas recommandées? Sinon pourquoi?
Pour plus de clarté, la question fait référence à un message pour un enregistrement automatique flake8( flake8.pycqa.org )
rakslice
Réponses:
232
La recommandation dans PEP-8 que vous rencontrez est:
Utilisez toujours une instruction def au lieu d'une instruction d'affectation qui lie une expression lambda directement à un nom.
Oui:
def f(x):return2*x
Non:
f =lambda x:2*x
La première forme signifie que le nom de l'objet fonction résultant est spécifiquement «f» au lieu du générique «<lambda>». Ceci est plus utile pour les traces et les représentations sous forme de chaîne en général. L'utilisation de l'instruction d'affectation élimine le seul avantage qu'une expression lambda peut offrir par rapport à une instruction def explicite (c'est-à-dire qu'elle peut être intégrée dans une expression plus grande)
L'attribution de lambdas à des noms ne fait que dupliquer la fonctionnalité de def - et en général, il est préférable de faire quelque chose d'une seule manière pour éviter la confusion et augmenter la clarté.
Le cas d'utilisation légitime de lambda est celui où vous souhaitez utiliser une fonction sans l'affecter, par exemple:
sorted(players, key=lambda player: player.rank)
En général, le principal argument contre cela est que les definstructions entraîneront plus de lignes de code. Ma principale réponse à cela serait: oui, et c'est très bien. À moins que vous ne codiez, minimiser le nombre de lignes n'est pas quelque chose que vous devriez faire: optez pour le clair plutôt que pour le court.
Je ne vois pas comment c'est pire. Le traçage va toujours inclure le numéro de ligne errant et le fichier source. L'un pourrait dire «f» tandis que l'autre dit «lambda». Peut-être que l'erreur lambda est plus facile à analyser car ce n'est pas un nom de fonction à un seul caractère ou un nom long mal nommé?
g33kz0r
4
@ g33kz0r Eh bien, bien sûr, si vous supposez que le reste de votre code sera de mauvaise qualité, suivre les conventions ne vous rapportera pas beaucoup. En général, non, ce n'est pas la fin du monde, mais c'est toujours une mauvaise idée.
Gareth Latty
40
Cette réponse n'est pas très utile, car lors de l'exécution de l'approche suggérée d'utilisation defvia le vérificateur PEP8, vous obtenez E704 multiple statements on one line (def), et si vous la divisez en deux lignes, vous obtenez E301 expected 1 blank line, found 0: - /
Adam Spiers
4
Je suis d'accord qu'il devrait être divisé. Mes points étaient que a) il n'est pas divisé dans le code de la réponse ci-dessus, provoquant E704, et b) si vous le divisez, vous avez besoin d'une ligne vierge laide au-dessus pour éviter E301.
Adam Spiers
3
J'utilise des lambdas lorsque je veux mettre l'accent sur une fonction pure (pas d'effets secondaires), et parfois je dois utiliser la même fonction à deux endroits, c'est-à-dire groupby et trier ensemble. Alors j'ignore cette convention.
manu
120
Voici l'histoire, j'avais une simple fonction lambda que j'utilisais deux fois.
a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)
C'est juste pour la représentation, j'ai fait face à deux versions différentes de cela.
Maintenant, pour garder les choses SEC, je commence à réutiliser ce lambda commun.
f =lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
À ce stade, mon vérificateur de qualité de code se plaint du fait que lambda est une fonction nommée, je la convertis donc en fonction.
def f(x):return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Maintenant, le vérificateur se plaint qu'une fonction doit être délimitée par une ligne vierge avant et après.
def f(x):return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Ici, nous avons maintenant 6 lignes de code au lieu de 2 lignes originales sans augmentation de la lisibilité et sans augmentation du caractère pythonique. À ce stade, le vérificateur de code se plaint que la fonction n'a pas de docstrings.
À mon avis, il vaut mieux éviter et enfreindre cette règle lorsque cela a du sens, utilisez votre jugement.
a = [x + offset for x in simple_list]. Pas besoin d'utiliser mapet lambdaici.
Georgy
9
@Georgy Je crois que le but était de déplacer la x + offsetpartie vers un emplacement abstrait qui peut être mis à jour sans changer plus d'une ligne de code. Avec les compréhensions de liste comme vous l'avez mentionné, vous auriez toujours besoin de deux lignes de code qui les contiendraient x + offsetjuste maintenant dans les compréhensions de liste. Afin de les extraire comme l'auteur le voulait, vous auriez besoin d'un fichier defou lambda.
Julian
1
@Julian En dehors de defet lambdaon pourrait aussi utiliser functools.partial : f = partial(operator.add, offset)et puis a = list(map(f, simple_list)).
Georgy
Qu'en est-il def f(x): return x + offset(c'est-à-dire une fonction simple définie sur une seule ligne)? Au moins avec flake8, je ne reçois pas de plaintes concernant les lignes vides.
DocOc
1
@Julian Dans certains cas, vous pouvez utiliser une compréhension imbriquée:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea
24
Lattyware a tout à fait raison: fondamentalement, PEP-8 veut que vous évitiez des choses comme
f =lambda x:2* x
et utilisez plutôt
def f(x):return2* x
Cependant, comme indiqué dans un récent rapport de bogue (août 2014), des déclarations telles que les suivantes sont désormais conformes:
a.f =lambda x:2* x
a["f"]=lambda x:2* x
Étant donné que mon vérificateur PEP-8 ne l'implémente pas encore correctement, j'ai désactivé E731 pour le moment.
Même lors de l'utilisation def, le vérificateur PEP8 se plaint E301 expected 1 blank line, found 0, vous devez donc ajouter une ligne vierge laide avant.
Adam Spiers
1
J'ai également rencontré une situation dans laquelle il était même impossible d'utiliser une fonction def (ined).
classSomeClass(object):# pep-8 does not allow this
f =lambda x: x +1# NOQAdef not_reachable(self, x):return x +1@staticmethoddef also_not_reachable(x):return x +1@classmethoddef also_not_reachable(cls, x):return x +1
some_mapping ={'object1':{'name':"Object 1",'func': f},'object2':{'name':"Object 2",'func': some_other_func},}
Dans ce cas, je voulais vraiment faire un mapping qui appartenait à la classe. Certains objets du mappage nécessitaient la même fonction. Il serait illogique de placer la fonction nommée en dehors de la classe. Je n'ai pas trouvé de moyen de faire référence à une méthode (méthode statique, méthode de classe ou normale) depuis l'intérieur du corps de la classe. SomeClass n'existe pas encore lorsque le code est exécuté. Il n'est donc pas non plus possible de s'y référer depuis la classe.
Vous pouvez faire référence also_not_reachabledans la définition de mappage commeSomeClass.also_not_reachable
yaccz
1
Je ne sais pas quel point vous essayez de faire valoir ici. Chacun de vos noms de fonction est aussi accessible que fdans les versions 2.7 et 3.5 pour moi
Eric
Non, toutes les fonctions, à l'exception de la fonction lambda, ne sont pas accessibles à partir du corps de la classe. Vous obtiendrez une AttributeError: l'objet de type 'SomeClass' n'a pas d'attribut '...' si vous essayez d'accéder à l'une de ces fonctions dans l'objet some_mapping.
simP
3
@simP tous sont parfaitement accessibles. Ceux avec @staticmethodet @classmethodn'ont pas besoin d'un objet, juste SomeClass.also_not_reachable(bien qu'ils aient besoin de noms distinctifs). Si vous avez besoin d'y accéder à partir de méthodes de classe, utilisez simplementself.also_not_reachable
ababak
@simP peut-être que vous devriez renommer vos *not_reachableméthodes en not_as_easily_reachable_from_class_definition_as_a_lambdaxD
flake8
( flake8.pycqa.org )Réponses:
La recommandation dans PEP-8 que vous rencontrez est:
L'attribution de lambdas à des noms ne fait que dupliquer la fonctionnalité de
def
- et en général, il est préférable de faire quelque chose d'une seule manière pour éviter la confusion et augmenter la clarté.Le cas d'utilisation légitime de lambda est celui où vous souhaitez utiliser une fonction sans l'affecter, par exemple:
En général, le principal argument contre cela est que les
def
instructions entraîneront plus de lignes de code. Ma principale réponse à cela serait: oui, et c'est très bien. À moins que vous ne codiez, minimiser le nombre de lignes n'est pas quelque chose que vous devriez faire: optez pour le clair plutôt que pour le court.la source
def
via le vérificateur PEP8, vous obtenezE704 multiple statements on one line (def)
, et si vous la divisez en deux lignes, vous obtenezE301 expected 1 blank line, found 0
: - /Voici l'histoire, j'avais une simple fonction lambda que j'utilisais deux fois.
C'est juste pour la représentation, j'ai fait face à deux versions différentes de cela.
Maintenant, pour garder les choses SEC, je commence à réutiliser ce lambda commun.
À ce stade, mon vérificateur de qualité de code se plaint du fait que lambda est une fonction nommée, je la convertis donc en fonction.
Maintenant, le vérificateur se plaint qu'une fonction doit être délimitée par une ligne vierge avant et après.
Ici, nous avons maintenant 6 lignes de code au lieu de 2 lignes originales sans augmentation de la lisibilité et sans augmentation du caractère pythonique. À ce stade, le vérificateur de code se plaint que la fonction n'a pas de docstrings.
À mon avis, il vaut mieux éviter et enfreindre cette règle lorsque cela a du sens, utilisez votre jugement.
la source
a = [x + offset for x in simple_list]
. Pas besoin d'utilisermap
etlambda
ici.x + offset
partie vers un emplacement abstrait qui peut être mis à jour sans changer plus d'une ligne de code. Avec les compréhensions de liste comme vous l'avez mentionné, vous auriez toujours besoin de deux lignes de code qui les contiendraientx + offset
juste maintenant dans les compréhensions de liste. Afin de les extraire comme l'auteur le voulait, vous auriez besoin d'un fichierdef
oulambda
.def
etlambda
on pourrait aussi utiliser functools.partial :f = partial(operator.add, offset)
et puisa = list(map(f, simple_list))
.def f(x): return x + offset
(c'est-à-dire une fonction simple définie sur une seule ligne)? Au moins avec flake8, je ne reçois pas de plaintes concernant les lignes vides.a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
Lattyware a tout à fait raison: fondamentalement, PEP-8 veut que vous évitiez des choses comme
et utilisez plutôt
Cependant, comme indiqué dans un récent rapport de bogue (août 2014), des déclarations telles que les suivantes sont désormais conformes:
Étant donné que mon vérificateur PEP-8 ne l'implémente pas encore correctement, j'ai désactivé E731 pour le moment.
la source
def
, le vérificateur PEP8 se plaintE301 expected 1 blank line, found 0
, vous devez donc ajouter une ligne vierge laide avant.J'ai également rencontré une situation dans laquelle il était même impossible d'utiliser une fonction def (ined).
Dans ce cas, je voulais vraiment faire un mapping qui appartenait à la classe. Certains objets du mappage nécessitaient la même fonction. Il serait illogique de placer la fonction nommée en dehors de la classe. Je n'ai pas trouvé de moyen de faire référence à une méthode (méthode statique, méthode de classe ou normale) depuis l'intérieur du corps de la classe. SomeClass n'existe pas encore lorsque le code est exécuté. Il n'est donc pas non plus possible de s'y référer depuis la classe.
la source
also_not_reachable
dans la définition de mappage commeSomeClass.also_not_reachable
f
dans les versions 2.7 et 3.5 pour moi@staticmethod
et@classmethod
n'ont pas besoin d'un objet, justeSomeClass.also_not_reachable
(bien qu'ils aient besoin de noms distinctifs). Si vous avez besoin d'y accéder à partir de méthodes de classe, utilisez simplementself.also_not_reachable
*not_reachable
méthodes ennot_as_easily_reachable_from_class_definition_as_a_lambda
xD