Объем лямбды функционирует и их параметры?

Ваше последнее предложение - лучшее.

if (foo != null && foo.bar()) {
    etc...
}

Потому что:

  1. Это легче читать.
  2. Это безопасно: foo.bar () никогда не будет выполняться, если foo == null.
  3. Это предотвращает плохую практику, такую ​​как перехват исключений NullPointerExceptions (в большинстве случаев из-за ошибки в вашем коде)
  4. Он должен выполняться так же быстро или даже быстрее, чем другие методы (хотя я думаю, что это должно почти невозможно это заметить).
79
задан martineau 1 June 2017 в 00:57
поделиться

8 ответов

Проблема в том, что переменная m (ссылка) берется из окружающей области. В лямбда-области содержатся только параметры.

Чтобы решить эту проблему, вам нужно создать другую область для лямбда:

def callback(msg):
    print msg

def callback_factory(m):
    return lambda: callback(m)

funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(callback_factory(m))
for f in funcList:
    f()

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

Или с помощью functools.partial :

from functools import partial

def callback(msg):
    print msg

funcList=[partial(callback, m) for m in ('do', 're', 'mi')]
for f in funcList:
    f()
71
ответ дан 24 November 2019 в 10:08
поделиться

Во-первых, то, что вы видите, не является проблемой и не связано с вызовом по ссылке или по значению.

Определенный вами лямбда-синтаксис не имеет параметров, и поэтому , объем, который вы видите с параметром m , является внешним по отношению к лямбда-функции. Вот почему вы видите такие результаты.

Лямбда-синтаксис в вашем примере не нужен, и вы бы предпочли использовать простой вызов функции:

for m in ('do', 're', 'mi'):
    callback(m)

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

В качестве примечания относительно передачи параметров. Параметры в Python всегда являются ссылками на объекты. Цитируя Алекса Мартелли:

Проблема терминологии может быть связана с тот факт, что в Python значение имя - это ссылка на объект. Итак, вы всегда передаете значение (нет неявное копирование), и это значение всегда ссылка. [...] Теперь, если вы хотите придумать для этого название, например "по объектной ссылке", "по нескопированным value "или что-то еще, будь моим гостем. Попытка повторно использовать терминологию, которая более широко применяется к языкам где «переменные - это ящики» для язык, где "переменные" теги "это, ИМХО, скорее запутает чем помочь.

0
ответ дан 24 November 2019 в 10:08
поделиться

Когда создается лямбда, она не копирует переменные во включающей области видимости, которую она использует. Он поддерживает ссылку на среду, чтобы позже можно было найти значение переменной. Всего один м . Он назначается каждый раз в цикле. После цикла переменная m имеет значение 'mi' . Поэтому, когда вы фактически запускаете функцию, которую вы создали позже, она будет искать значение m в среде, которая ее создала, которая к тому времени будет иметь значение 'mi' .

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

for m in ('do', 're', 'mi'):
    funcList.append(lambda m=m: callback(m))
121
ответ дан 24 November 2019 в 10:08
поделиться

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

0
ответ дан 24 November 2019 в 10:08
поделиться

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

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

def callback(msg):
    print msg

def createCallback(msg):
    return lambda: callback(msg)

#creating a list of function handles with an iterator
funcList=[]
for m in ('do', 're', 'mi'):
    funcList.append(createCallback(m))
for f in funcList:
    f()

Вывод:

do
re
mi
0
ответ дан 24 November 2019 в 10:08
поделиться

Python, конечно, использует ссылки, но в данном случае это не имеет значения. контекст.

Когда вы определяете лямбда (или функцию, поскольку это точно такое же поведение), она не оценивает лямбда-выражение до выполнения:

# defining that function is perfectly fine
def broken():
    print undefined_var

broken() # but calling it will raise a NameError

Еще более удивительно, чем ваш пример лямбда:

i = 'bar'
def foo():
    print i

foo() # bar

i = 'banana'

foo() # you would expect 'bar' here? well it prints 'banana'

Короче , думайте динамически: перед интерпретацией ничего не оценивается, поэтому ваш код использует последнее значение m.

Когда он ищет m при выполнении лямбда, m берется из самой верхней области видимости, что означает, что, как указывали другие ; вы можете обойти эту проблему, добавив другую область видимости:

def factory(x):
    return lambda: callback(x)

for m in ('do', 're', 'mi'):
    funcList.append(factory(m))

Здесь, когда лямбда вызывается, она ищет x в области определения лямбды. Этот x - локальная переменная, определенная в теле фабрики. По этой причине значение, используемое при выполнении лямбда-выражения, будет значением, переданным в качестве параметра во время вызова factory. И дореми!

В качестве примечания, я мог бы определить factory как factory (m) [заменить x на m], поведение такое же. Я использовал другое имя для ясности :)

Вы можете обнаружить, что Андрей Бауэр получил похожие лямбда-проблемы. Что интересно в этом блоге, так это комментарии, где вы узнаете больше о закрытии python :)

Этот x - локальная переменная, определенная в теле фабрики. По этой причине значение, используемое при выполнении лямбда-выражения, будет значением, переданным в качестве параметра во время вызова factory. И дореми!

В качестве примечания, я мог бы определить factory как factory (m) [заменить x на m], поведение такое же. Я использовал другое имя для ясности :)

Вы можете обнаружить, что Андрей Бауэр получил похожие лямбда-проблемы. Что интересно в этом блоге, так это комментарии, где вы узнаете больше о закрытии python :)

Этот x - локальная переменная, определенная в теле фабрики. По этой причине значение, используемое при выполнении лямбда-выражения, будет значением, переданным в качестве параметра во время вызова factory. И дореми!

В качестве примечания, я мог бы определить factory как factory (m) [заменить x на m], поведение такое же. Я использовал другое имя для ясности :)

Вы можете обнаружить, что Андрей Бауэр получил похожие лямбда-проблемы. Что интересно в этом блоге, так это комментарии, где вы узнаете больше о закрытии python :)

)

Вы можете обнаружить, что Андрей Бауэр получил похожие лямбда-проблемы. Что интересно в этом блоге, так это комментарии, где вы узнаете больше о закрытии python :)

)

Вы можете обнаружить, что Андрей Бауэр получил похожие лямбда-проблемы. Что интересно в этом блоге, так это комментарии, где вы узнаете больше о закрытии python :)

6
ответ дан 24 November 2019 в 10:08
поделиться

Да, это проблема области видимости, она привязывается к внешнему m, независимо от того, используете ли вы лямбду или локальную функцию. Вместо этого используйте функтор:

class Func1(object):
    def __init__(self, callback, message):
        self.callback = callback
        self.message = message
    def __call__(self):
        return self.callback(self.message)
funcList.append(Func1(callback, m))
0
ответ дан 24 November 2019 в 10:08
поделиться

Не имеет прямого отношения к рассматриваемому вопросу, но тем не менее является бесценной мудростью: Объекты Python Фредрика Лунда.

1
ответ дан 24 November 2019 в 10:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: