E731 не призначають лямбда-вираз, використовуйте def


193

Я отримую це попередження pep8, коли я використовую лямбда-вирази. Чи не рекомендуються лямбда-вирази? Якщо ні, чому?


4
Для наочності питання стосується повідомлення для автоматизованої реєстрації flake8( flake8.pycqa.org )
rakslice

Відповіді:


231

Рекомендація в PEP-8, яку ви працюєте, це:

Завжди використовуйте оператор def замість оператора присвоєння, який прив'язує лямбда-вираз безпосередньо до імені.

Так:

def f(x): return 2*x 

Немає:

f = lambda x: 2*x 

Перша форма означає, що ім'я результуючого функціонального об'єкта конкретно 'f' замість загального '<lambda>'. Це корисніше для зворотних зворотів та подання рядків загалом. Використання оператора присвоєння виключає єдину вигоду, яку може запропонувати лямбда-вираз над явним оператором def (тобто, що він може бути вбудований у більший вираз)

Присвоєння лямбдам імен в основному просто дублює функціональність def- і взагалі, найкраще зробити щось єдиним способом, щоб уникнути плутанини та підвищення чіткості.

Законний випадок використання лямбда - це те, де ви хочете використовувати функцію, не призначаючи її, наприклад:

sorted(players, key=lambda player: player.rank)

Загалом, головний аргумент проти цього полягає в тому, що у defтвердженнях з'явиться більше рядків коду. Моя основна відповідь на це була б: так, і це нормально. Якщо ви не кодуєте гольф, мінімізація кількості ліній - це не те, що вам слід робити: займіться ясним за короткий час.


5
Я не бачу, як це гірше. У зворотному відстеженні все ще буде включений номер рядка помилки та вихідний файл. Можна сказати "f", тоді як інший говорить "лямбда". Може бути, помилку лямбда легше сканувати, оскільки це не односимвольне ім'я функції або невдало назване довге ім’я?
g33kz0r

4
@ g33kz0r Ну, звичайно, якщо ви припускаєте, що решта вашого коду має низьку якість, наступні конвенції не принесуть вам великої ваги. Взагалі, ні, це не кінець світу, але все одно це погана ідея.
Гарет Летті

39
Ця відповідь не дуже корисна, оскільки, виконуючи запропонований підхід використання defчерез перевірку PEP8, ви отримуєте E704 multiple statements on one line (def), і якщо розділити його на два рядки, ви отримаєте E301 expected 1 blank line, found 0: - /
Адам Шпіерс

4
Я згоден, це слід розділити. Мої моменти полягали в тому, що: а) це не розбиття в коді відповіді вище, що спричиняє E704, і б) якщо ви розділите його, вам потрібен некрасивий порожній рядок над ним, щоб уникнути E301.
Адам Шпіерс

3
Я використовую лямбда, коли хочу підкреслити чисту функцію (відсутність побічних ефектів), і іноді мені доводиться використовувати одну і ту ж функцію в двох місцях, тобто групувати і сортувати разом. Тому я ігнорую цю умову.
Мана

119

Ось історія, у мене була проста лямбда-функція, яку я використовував двічі.

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 рядків, не збільшуючи читабельності та не збільшуючись, будучи пітонічними. У цей момент програма перевірки коду скаржиться на те, що функція не має документації.

На мою думку, цього правила краще уникати та порушувати, коли це має сенс, використовувати своє судження.


13
a = [x + offset for x in simple_list]. Не потрібно використовувати mapі lambdaтут.
Георгій

8
@Georgy Я вважаю, що справа полягала в тому, щоб перемістити x + offsetчастину до абстрагованого місця, яке можна оновити, не змінюючи більше одного рядка коду. Як ви вже згадали список розуміння списку, вам все одно знадобляться два рядки коду, які x + offsetміститимуть їх, вони зараз будуть в списку розумінь. Для того, щоб витягнути їх так, як хотів автор, вам знадобиться defабо lambda.
Джуліан

1
@Julian Окрім defта, lambdaможна також використовувати functools.partial : f = partial(operator.add, offset)а потім a = list(map(f, simple_list)).
Георгій

А як щодо def f(x): return x + offset(тобто простої функції, визначеної в одному рядку)? Принаймні, з flake8 я не отримую скарг на порожні рядки.
DocOc

1
@Julian У деяких випадках ви можете використовувати вкладене розуміння:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea

24

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.


8
Навіть коли ви користуєтеся def, шашка PEP8 скаржиться на E301 expected 1 blank line, found 0, тому вам доведеться перед цим додати некрасиву порожню лінію.
Адам Шпіерс

1

Я також зіткнувся з ситуацією, в якій навіть неможливо було використовувати функцію 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},
  }

У цьому випадку мені дуже хотілося зробити відображення, яке належало до класу. Деякі об’єкти в картографуванні потребували тієї ж функції. Було б нелогічно ставити названу функцію поза класом. Я не знайшов способу звернутися до методу (staticmethod, classmethod або normal) зсередини тіла класу. SomeClass ще не існує при запуску коду. Тому посилання на нього з класу також неможливо.


Ви можете вказати also_not_reachableу визначенні відображення якSomeClass.also_not_reachable
yaccz

1
Я не знаю, що ви тут намагаєтеся зробити. Кожне з ваших імен функцій доступне так само, fяк і для мене 2,7 та 3,5
Ерік

Ні, всі функції, за винятком лямбда-функції, недоступні в тілі класу. Ви отримаєте AttributeError: тип об’єкта 'SomeClass' не має атрибута '...', якщо ви спробуєте отримати доступ до однієї з цих функцій в об'єкт some_mapping.
simP

3
@simP всі вони ідеально доступні. Ті, що мають @staticmethodі @classmethodне потребують об'єкт, просто SomeClass.also_not_reachable(хоча їм потрібні відмінні назви). Якщо вам потрібно отримати доступ до них з методів класу, просто використовуйтеself.also_not_reachable
ababak

@simP, можливо, ви повинні перейменувати свою *not_reachable методи як not_as_easily_reachable_from_class_definition_as_a_lambdaxD
Romain Vincent
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.