Почему назначения не допускаются в выражениях Python `lambda`? - PullRequest
0 голосов
/ 29 апреля 2018

Это не дубликат Назначения внутри лямбда-выражения в Python , т. Е. Я не спрашиваю, как обмануть Python в присвоении в выражении lambda.

У меня есть фон λ-исчисления. Учитывая следующий код, это похоже, что Python вполне готов выполнять побочные эффекты в lambda выражения:

#!/usr/bin/python

def applyTo42(f):
    return f(42)

def double(x):
    return x * 2

class ContainsVal:
    def __init__(self, v):
        self.v = v

    def store(self, v):
        self.v = v

def main():

    print('== functional, no side effects')

    print('-- print the double of 42')
    print(applyTo42(double))

    print('-- print 1000 more than 42')
    print(applyTo42(lambda x: x + 1000))

    print('-- print c\'s value instead of 42')
    c = ContainsVal(23)
    print(applyTo42(lambda x: c.v))


    print('== not functional, side effects')

    print('-- perform IO on 42')
    applyTo42(lambda x: print(x))

    print('-- set c\'s value to 42')
    print(c.v)
    applyTo42(lambda x: c.store(x))
    print(c.v)

    #print('== illegal, but why?')
    #print(applyTo42(lambda x: c.v = 99))

if __name__ == '__main__':
    main()

Но если я раскомментирую строки

    print('== illegal, but why?')
    print(applyTo42(lambda x: c.v = 99))

Я получу

SyntaxError: lambda cannot contain assignment

Почему бы и нет? Какова более глубокая причина этого?

  • Как показывает код, речь не идет о «чистоте» в функциональный смысл.

  • Единственное объяснение, которое я могу себе представить, заключается в том, что вернуть что угодно, даже не None. Но это звучит глупо и будет легко исправить (один из способов: вернуть лямбда-выражения None, если тело является заявлением).

Не ответ:

  • Потому что это определено именно так (я хочу знать, почему оно определено именно так).

  • Потому что это в грамматике (см. Выше).

  • Используйте def, если вам нужны заявления (я не спрашивал, как получить операторы в функцию).

«Это изменило бы синтаксис / язык / семантику», было бы неплохо в качестве ответа, если вы можете придумать пример такого изменения и почему это будет плохо.

Ответы [ 7 ]

0 голосов
/ 10 мая 2018

Я думаю, что все ребята уже ответили на это. В основном мы используем функцию лямбда-выражений, когда просто хотим:

-создание некоторых простых функций, которые отлично выполняют работу в определенном месте (большую часть времени скрыто внутри некоторых других больших функций -Лямбда-функция не имеет имени -Можно использовать с некоторыми другими встроенными функциями, такими как map, list и т. Д. ...

>>> Celsius = [39.2, 36.5, 37.3, 37.8] 
>>> Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius) # mapping the list here  
>>> print Fahrenheit
[102.56, 97.700000000000003, 99.140000000000001, 100.03999999999999]

Пожалуйста, посетите эту веб-страницу, это может быть полезно. https://www.python -course.eu / lambda.php

0 голосов
/ 12 мая 2018

Пока lambdaeval()) разрешено внутри *1003*, вы можете выполнять назначения внутри lambda:

q = 3

def assign(var_str, val_str):
    exec("global " + var_str + "; " + 
    var_str + " = " + val_str)

lambda_assign = lambda var_str, val_str: assign(var_str, val_str)

q ## gives: 3

lambda_assign("q", "100")

q ## gives: 100

## what would such expression be a win over the direct:

q = 100

## ? `lambda_assign("q", "100")` will be for sure slower than
##   `q = 100` isn't it?

q_assign = lambda v: assign("q", v)

q_assign("33")

q ## 33

## but do I need lambda for q_assign?

def q_assign(v): assign("q", v) 

## would do it, too, isn't it?

Но поскольку лямбда-выражения позволяют определять только 1 выражение внутри их тела (по крайней мере, в Python ...), какой смысл разрешать присваивание внутри лямбды? Чистым эффектом будет назначение напрямую (без использования лямбды) q = 100, не так ли?

Это было бы даже быстрее, чем делать это через определенную лямбду, поскольку у вас есть хотя бы одна функция поиска и выполнения меньше для выполнения ...

0 голосов
/ 07 мая 2018

На самом деле нет более глубоких причин, это не имеет ничего общего с лямбда-структурами или конструкциями функциональных языков, это просто для того, чтобы программисты не смешивали операторы = и ==, что является очень распространенной ошибкой в ​​других языках

Если есть еще кое-что в этой истории, я думаю, как и MAYBE, потому что python bdfl GVR выразил свои нелюбимые стороны к лямбде и другим функциональным функциям и попытался (и допустил) полностью удалить их из python 3 https://www.artima.com/weblogs/viewpost.jsp?thread=98196

На момент написания этой статьи основные разработчики в последнее время обсуждали вопрос о том, следует ли включать ограниченное присваивание именных выражений, дебаты все еще продолжаются, поэтому, возможно, когда-нибудь мы можем увидеть это в лямбде (маловероятно)

Поскольку вы сказали это сами, это определенно не о побочных эффектах или чистоте, они просто не хотят, чтобы лямбда была чем-то большим, чем одно выражение ... ... ...

С учетом вышесказанного, здесь есть кое-что о множественных выражениях в лямбде, читайте дальше, если вам интересно

Это вовсе не невозможно в python, на самом деле иногда было необходимо захватывать поздние привязки переменных и обходить их (ab), используя kwargs (аргументы ключевого слова)

редактирование:

пример кода

f = lambda x,a=1: (lambda c = a+2, b = a+1: (lambda e = x,d = c+1: print(a,b,c,d,e))())()

f("w")

# output 1 2 3 4 w

# expression assignment through an object's method call

if let(a=1) .a > 0 and let(b=let.a+1) .b != 1 and let(c=let.b+let.a) .c:
    print(let.a, let.b, let.c)

# output 1 2 3
0 голосов
/ 05 мая 2018

Мой ответ основан на комментарии Чепнера выше и не опирается на какой-либо другой заслуживающий доверия или официальный источник, однако я думаю, что он будет полезен.

Если бы в лямбда-выражениях было разрешено присваивание, то ошибка смешения == (проверка на равенство) с = (присваивание) имела бы больше шансов уйти в дикую природу.

Пример:

>>> # Correct use of equality test
... list(filter(lambda x: x==1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[1, 1.0, (1+0j)]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is always None
... list(filter(lambda x: None, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is the assigned value
... list(filter(lambda x: 1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[0, 1, 0.0, 1.0, 0j, (1+0j)]
0 голосов
/ 29 апреля 2018

Вся причина lambda в том, что это выражение. 1 Если вам нужно что-то вроде lambda, но это утверждение, это просто def.

Выражения Python не могут содержать операторов. Это, на самом деле, основополагающий для языка, и Python получает много пользы от этого решения. Это причина того, что отступ для управления потоком работает, а не неуклюж, как во многих других попытках (например, CoffeeScript). Это причина, по которой вы можете считывать изменения состояния, просматривая первый объект в каждой строке. Это даже одна из причин, по которой язык легко разбирается как для компилятора, так и для читателей. 2

Изменение в Python каким-либо способом «избежать» разрыва выражения-выражения, за исключением, может быть, очень осторожного и ограниченного способа, превратит его в совершенно другой язык, который больше не имел многих преимуществ, которые вызывают люди выбирают Python в первую очередь.

Изменение Python для создания большинства выражений операторов (например, Ruby) снова превратит его в совершенно другой язык без текущих преимуществ Python.

И если Python сделал внесет любое из этих изменений, то больше не было бы причины для lambda, во-первых; 2,3 , вы могли бы просто использовать def операторы внутри выражения.


А как насчет изменения Python вместо выражения выражений? Ну, должно быть очевидно, что это сломало бы: «Вы можете считывать изменения состояния, просматривая первый объект в каждой строке». Хотя обычно Гвидо фокусируется на том факте, что if spam=eggs является ошибкой чаще, чем полезной вещью.

Тот факт, что Python дает вам возможность обойти это при необходимости, например, setattr или даже явный вызов __setitem__ на globals(), не означает, что он должен иметь прямую синтаксическую поддержку. То, что очень редко требуется, не заслуживает синтаксического сахара - и даже более того, для чего-то достаточно необычного, что оно должно поднимать брови и / или красные флаги, когда это действительно делается.


1. Я понятия не имею, было ли это пониманием Гвидо, когда он первоначально добавил lambda в Python 1.0. Но это определенно причина, по которой lambda не был удален в Python 3.0.

2. На самом деле, Гвидо неоднократно высказывал предположение, что разрешение синтаксического анализатора LL (1), который люди могут запускать в своих головах, является достаточной причиной для того, чтобы язык основывался на утверждениях, до такой степени, что другие преимущества даже не нужно обсуждать , Я писал об этом несколько лет назад , если кому-то интересно.

3. Если вам интересно, почему так много языков do имеют выражение lambda несмотря на то, что уже имеют def: во многих языках, от C ++ до Ruby, функции не являются первоклассными объектами, которые могут быть обошлось, поэтому им пришлось изобрести вторую вещь, которая является первоклассной, но работает как функция. В других, от Smalltalk до Java, функции даже не существуют , только методы, поэтому им снова пришлось изобрести вторую вещь, которая не является методом, а работает как один. У Python нет ни одной из этих проблем.

4. Некоторые языки, такие как C # и JavaScript, фактически имели прекрасно работающие определения встроенных функций, но добавили некоторый синтаксис lambda в качестве чистого синтаксического сахара, чтобы сделать его более кратким и менее наглядным. Это может на самом деле стоить делать в Python (хотя каждая попытка хорошего синтаксиса до сих пор терпела неудачу), но это не будет текущий синтаксис lambda, который почти столь же многословен, как def.

0 голосов
/ 29 апреля 2018

В своем нынешнем виде Python был разработан как язык, основанный на выражениях. Поэтому присваивания и другие привязки имен являются операторами и не имеют никакого результата.

Разработчики ядра Python в настоящее время обсуждают PEP 572, который ввел бы выражение для привязки имени .

0 голосов
/ 29 апреля 2018

Существует проблема синтаксиса : присваивание является оператором , а тело лямбды может содержать только выражений . Синтаксис Python разработан таким образом 1 . Проверьте это в https://docs.python.org/3/reference/grammar.html.

Существует также проблема семантики : что каждый оператор возвращает ?

Я не думаю, что есть интерес к изменению этого, поскольку лямбды предназначены для очень простого и короткого кода. Более того, оператор допускает также последовательностей операторов, что нежелательно для лямбд.

Это также может быть исправлено путем выборочного разрешения определенных операторов в теле лямбды и указания семантики (например, присваивание возвращает None или возвращает присвоенное значение; последнее имеет для меня больше смысла). Но в чем выгода?

Лямбды и функции взаимозаменяемы. Если у вас действительно есть вариант использования для конкретного оператора в теле лямбды, вы можете определить функцию, которая его выполняет, и ваша конкретная проблема решена.


Возможно, вы можете создать синтаксический макрос, чтобы разрешить это с MacroPy3 (я просто догадываюсь, так как я фанат проекта, но все же у меня не было времени погрузиться в это).

Например, MacroPy позволит вам определить макрос, который преобразует f[_ * _] в lambda a, b: a * b, поэтому не должно быть невозможности определить синтаксис для лямбды, которая вызывает определенную вами функцию.


1 Хорошая причина не менять его - это то, что он нарушит синтаксис, потому что лямбда может быть в местах, где могут быть выражения. И заявления не должны. Но это мое субъективное замечание.

...