Recebo esse aviso pep8 sempre que uso expressões lambda. As expressões lambda não são recomendadas? Se não, por quê?
Recebo esse aviso pep8 sempre que uso expressões lambda. As expressões lambda não são recomendadas? Se não, por quê?
Respostas:
A recomendação no PEP-8 em que você está se deparando é:
Sempre use uma declaração def em vez de uma declaração de atribuição que vincule uma expressão lambda diretamente a um nome.
Sim:
def f(x): return 2*x
Não:
f = lambda x: 2*x
A primeira forma significa que o nome do objeto de função resultante é especificamente 'f' em vez do genérico '<lambda>'. Isso é mais útil para tracebacks e representações de string em geral. O uso da declaração de atribuição elimina o único benefício que uma expressão lambda pode oferecer sobre uma declaração def explícita (ou seja, que pode ser incorporada a uma expressão maior)
A atribuição de lambdas a nomes basicamente apenas duplica a funcionalidade do def
- e, em geral, é melhor fazer algo de uma única maneira para evitar confusão e aumentar a clareza.
O caso de uso legítimo para lambda é onde você deseja usar uma função sem atribuí-la, por exemplo:
sorted(players, key=lambda player: player.rank)
Em geral, o principal argumento contra fazer isso é que as def
instruções resultarão em mais linhas de código. Minha principal resposta a isso seria: sim, e tudo bem. A menos que você esteja praticando golfe com código, minimizar o número de linhas não é algo que você deve fazer: vá além do óbvio.
def
pelo uso do verificador PEP8, você obtém E704 multiple statements on one line (def)
e, se a dividir em duas linhas, obtém E301 expected 1 blank line, found 0
: - /
Aqui está a história, eu tinha uma função lambda simples que estava usando duas vezes.
a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)
Isto é apenas para a representação, eu já enfrentei duas versões diferentes disso.
Agora, para manter as coisas secas, começo a reutilizar esse lambda comum.
f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Nesse ponto, meu verificador de qualidade de código reclama que lambda é uma função nomeada, então eu a converto em uma função.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Agora, o verificador reclama que uma função deve ser delimitada por uma linha em branco antes e depois.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Aqui temos agora 6 linhas de código, em vez das 2 linhas originais, sem aumento na legibilidade e sem aumento de python. Neste ponto, o verificador de código reclama da função que não possui docstrings.
Na minha opinião, é melhor evitar e quebrar essa regra quando faz sentido, use seu julgamento.
a = [x + offset for x in simple_list]
. Não há necessidade de usar map
e lambda
aqui.
x + offset
parte para um local abstraído que possa ser atualizado sem alterar mais de uma linha de código. Com as compreensões de lista, como você mencionou, você ainda precisaria de duas linhas de código que continham x + offset
que agora estariam nas compreensões de lista. Para retirá-las como o autor queria, você precisaria de um def
ou lambda
.
def
e lambda
também se poderia usar functools.partial : f = partial(operator.add, offset)
e então a = list(map(f, simple_list))
.
def f(x): return x + offset
(isto é, uma função simples definida em uma única linha)? Pelo menos com flake8, não recebo reclamações sobre linhas em branco.
a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
O Lattyware está absolutamente certo: basicamente o PEP-8 quer que você evite coisas como
f = lambda x: 2 * x
e use
def f(x):
return 2 * x
No entanto, conforme abordado em um relatório de bug recente (agosto de 2014), instruções como as seguintes agora são compatíveis:
a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x
Como meu verificador PEP-8 ainda não implementa isso corretamente, desliguei o E731 por enquanto.
def
, o verificador PEP8 está em conformidade E301 expected 1 blank line, found 0
, então você precisa adicionar uma linha em branco feia antes dele.
Também encontrei uma situação em que era impossível usar uma função 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},
}
Nesse caso, eu realmente queria fazer um mapeamento que pertencia à classe. Alguns objetos no mapeamento precisavam da mesma função. Seria ilógico colocar a função nomeada fora da classe. Não encontrei uma maneira de me referir a um método (staticmethod, classmethod ou normal) de dentro do corpo da classe. SomeClass ainda não existe quando o código é executado. Portanto, referir-se a ele da classe também não é possível.
also_not_reachable
na definição de mapeamento comoSomeClass.also_not_reachable
f
em 2.7 e 3.5 para mim #
@staticmethod
e @classmethod
não precisam de um objeto, apenas SomeClass.also_not_reachable
(embora precisem de nomes distintos). Se você precisar acessá-los a partir de métodos de classe é só usarself.also_not_reachable
*not_reachable
métodos como not_as_easily_reachable_from_class_definition_as_a_lambda
xD
flake8
( flake8.pycqa.org )