Рассмотрим следующий пример:

test = 123
f = lambda x,test=test: print(x, test)
del test
f('hello')

Отпечатки

hello 123

При захвате переменной в лямбда-определении первоначальная переменная, похоже, сохраняется.

Можно ли использовать лямбда в порядке , когда вы также можете использовать простой объект для хранения тех же данных?

class Test:
    def __init__(self, some_data):
        self.some_data = some_data

    def f(self, x):
        print(x, self.some_data)

t = Test('123')
t.f('hello')
0
Stefan 8 Окт 2018 в 12:35

2 ответа

Лучший ответ

При захвате переменной в лямбда-определении первоначальная переменная, похоже, сохраняется.

Поскольку вы сделали его значением по умолчанию для аргумента test лямбда-выражения, а Python оценивает значения по умолчанию для аргументов только один раз при создании функции. FWIW, это не имеет ничего общего с использованием ключевого слова lambda - lambda - это просто синтаксический сахар, и вы получите точно такой же результат с "полномасштабной" функцией, то есть:

test = 123
def f(x, test=test):
    print(x, test)
test = 456
f('hello')

Можно ли использовать лямбда, если вы также можете использовать простой объект для хранения тех же данных?

На самом деле, апельсины и яблоки. Функция предназначена не для «хранения» чего-либо, а для выполнения некоторых вычислений. Тот факт, что он может захватывать некоторые значения либо через значения аргументов по умолчанию, либо через закрытие, хотя и весьма полезен для некоторых случаев использования, не означает, что он заменяет правильные объекты или коллекции. Итак, ответ: зависит от того, что вы хотите делать с этими значениями и вашей функцией.

NB: технически то, что вы здесь делаете (почти), известно как «частичное приложение» (вам просто нужно переименовать Test.f в Test.__call__ и использовать t("hello") во втором пример)). Частичное применение функции - это механизм, с помощью которого функция с N аргументами при вызове с N - x (с x

def foo(a, b=None):
   if b is None:
       return lambda b, a=a: foo(b, a)
   return a + b

# here, `f` is a partial application of function `foo` to `1`
f = foo(1)
print(f)
f(2)

В этом случае мы используем замыкание для захвата a в традиции функционального программирования - «частичное приложение» также в основном является концепцией функционального программирования FWIW. Теперь, хотя он поддерживает некоторые функции и идиомы FP, Python - это, прежде всего, объектно-ориентированный язык, а поскольку замыкания являются эквивалентом объектов FP (замыкания - это способ объединить состояние и поведение вместе), также имеет смысл реализовать частичное приложение. в качестве надлежащего класса, либо со специальными "специальными" объектами (ваш класс Test, но также с объектами Method), либо с более общим "частичным" классом - , который уже существует в stdlib как functools.partial

1
bruno desthuilliers 8 Окт 2018 в 10:11

Значение аргумента по умолчанию для аргумента вычисляется один раз, когда определяется функция или лямбда. Это не оценивается на каждом сайте вызова. (Вот почему вы обычно не можете использовать [] в качестве аргумента по умолчанию и должны указывать вместо него None, а также писать код для выделения нового списка при каждом вызове функции.)

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

1
Florian Weimer 8 Окт 2018 в 10:22