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 не применимо и должно быть отключено.

Другие вопросы по тегам