E731 не назначайте лямбда-выражение, используйте def
Я получаю это предупреждение pep8 всякий раз, когда я использую лямбда-выражения. Лямбда-выражения не рекомендуются? Если нет, то почему?
7 ответов
Рекомендация в PEP-8, с которой вы сталкиваетесь:
Всегда используйте оператор def вместо оператора присваивания, который связывает лямбда-выражение непосредственно с именем.
Да:
def f(x): return 2*x
Нет:
f = lambda x: 2*x
Первая форма означает, что имя получающегося функционального объекта определенно 'f' вместо общего '
'. Это более полезно для трассировок и строковых представлений в целом. Использование оператора присваивания исключает единственное преимущество, которое лямбда-выражение может предложить по сравнению с явным оператором def (то есть то, что оно может быть встроено в большее выражение)
Присвоение лямбды именам в основном просто дублирует функциональность def
- и вообще, лучше сделать что-то одним способом, чтобы избежать путаницы и повысить ясность.
Законный вариант использования лямбды - это то, где вы хотите использовать функцию без ее назначения, например:
sorted(players, key=lambda player: player.rank)
Для простых операций operator
Модуль предоставляет несколько полезных опций в attrgetter
, itemgetter
а также methodcaller
который часто может заменить labmdas, которые просто обращаются к атрибуту (ам), элементу (ам) и к вызывающим методам.
Например, вышесказанное можно сделать с operator.attrgetter
вот так:
sorted(players, key=operator.attrgetter('rank'))
Вот история, у меня была простая лямбда-функция, которую я использовал дважды.
a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)
Это только для представления, я сталкивался с несколькими различными версиями этого.
Теперь, чтобы сохранить вещи СУХОЙ, я начинаю использовать эту обычную лямбду.
f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
В этот момент моя программа проверки качества кода жалуется на то, что лямбда является именованной функцией, поэтому я преобразую ее в функцию.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Теперь контролер жалуется, что функция должна быть ограничена одной пустой строкой до и после.
def f(x):
return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)
Здесь у нас теперь есть 6 строк кода вместо исходных 2 строк без увеличения читабельности и увеличения числа пифонов. На этом этапе программа проверки кода жалуется на то, что функция не имеет строк документации.
По моему мнению, это правило лучше избегать и нарушать, когда оно имеет смысл, используйте свое суждение.
Lattyware абсолютно прав: в основном PEP-8 хочет, чтобы вы избегали таких вещей, как
f = lambda x: 2 * x
и вместо этого использовать
def f(x):
return 2 * x
Однако, как указано в недавнем отчете об ошибках (август 2014 г.), такие утверждения, как следующие, теперь соответствуют:
a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x
Поскольку моя программа проверки PEP-8 еще не реализовала это правильно, я на время отключил E731.
Я также столкнулся с ситуацией, в которой было даже невозможно использовать функцию 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},
}
В этом случае я действительно хотел сделать отображение, которое принадлежало классу. Некоторые объекты в отображении нуждались в той же функции. Было бы нелогично помещать именованную функцию вне класса. Я не нашел способ ссылаться на метод (статический метод, метод класса или обычный) изнутри тела класса. SomeClass еще не существует, когда код выполняется. Так что ссылаться на него из класса тоже невозможно.
В дополнение к ответу Гарета Лэтти, еще одной причиной предпочтения определений является безопасность типов.
Следующий код пройдет проверку типа mypy, несмотря на то, что он содержит ошибку типа:
y = lambda x: x**2
print(y("fred"))
Мы можем сделать его типобезопасным, используя аннотированный код ниже, и теперь mypy обнаружит ошибку, как и ожидалось.
from typing import Callable
y: Callable[[int], int] = lambda x: x**2
print(y("fred"))
Однако это выглядит немного громоздко. Давайте сравним с типобезопасной альтернативой ниже.
def y(x: int) -> int:
return x**2
print(y("fred"))
Спорно,def
версия более читабельна и лаконична (объективно, хоть и занимает две строки, но в целом символов меньше и не требует дополнительного импорта).
Попробуй с этим, работает.
def probe(x: int, y:int) -> int: return x+y
Лямбды можно использовать для ленивых вычислений, таким образом откладывая некоторые дорогостоящие операции до тех пор, пока их результаты не потребуются.
Я просто столкнулся с одним случаем (с проблемой конкуренции / практики в коде), где я вычислял относительно дорогие функции pow() (относительно дорогие, поскольку входные данные состояли из полумиллиона тестовых случаев) для 3 различных случаев и определенных комбинаций из 3 случаев. Для ясности кода я бы вычислил все 3 случая, а затем возвратил бы комбинацию из 3, которые фактически были необходимы для текущего запроса.
К сожалению, это генерировало TLE ("превышено ограничение по времени") на определенных входах.
Используя лямбда-выражения для отсрочки дорогостоящих операций pow(), я смог решить проблемы с TLE, поскольку фактически вызывались только те вычисления, которые имели отношение к текущему запросу.
Поэтому я думаю, что это тот случай, когда предупреждение E731 не применимо и должно быть отключено.