Ваше последнее предложение - лучшее.
if (foo != null && foo.bar()) {
etc...
}
Потому что:
Проблема в том, что переменная 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()
Во-первых, то, что вы видите, не является проблемой и не связано с вызовом по ссылке или по значению.
Определенный вами лямбда-синтаксис не имеет параметров, и поэтому , объем, который вы видите с параметром m
, является внешним по отношению к лямбда-функции. Вот почему вы видите такие результаты.
Лямбда-синтаксис в вашем примере не нужен, и вы бы предпочли использовать простой вызов функции:
for m in ('do', 're', 'mi'):
callback(m)
Опять же, вы должны быть очень точны в том, какие лямбда-параметры вы используете и где именно их область действия начинается и заканчивается.
В качестве примечания относительно передачи параметров. Параметры в Python всегда являются ссылками на объекты. Цитируя Алекса Мартелли:
Проблема терминологии может быть связана с тот факт, что в Python значение имя - это ссылка на объект. Итак, вы всегда передаете значение (нет неявное копирование), и это значение всегда ссылка. [...] Теперь, если вы хотите придумать для этого название, например "по объектной ссылке", "по нескопированным value "или что-то еще, будь моим гостем. Попытка повторно использовать терминологию, которая более широко применяется к языкам где «переменные - это ящики» для язык, где "переменные" теги "это, ИМХО, скорее запутает чем помочь.
Когда создается лямбда, она не копирует переменные во включающей области видимости, которую она использует. Он поддерживает ссылку на среду, чтобы позже можно было найти значение переменной. Всего один м
. Он назначается каждый раз в цикле. После цикла переменная m
имеет значение 'mi'
. Поэтому, когда вы фактически запускаете функцию, которую вы создали позже, она будет искать значение m
в среде, которая ее создала, которая к тому времени будет иметь значение 'mi'
.
Одним из распространенных и идиоматических решений этой проблемы является захват значения m
в момент создания лямбда, используя его в качестве аргумента по умолчанию необязательного параметра. Обычно вы используете параметр с тем же именем, поэтому вам не нужно изменять тело кода:
for m in ('do', 're', 'mi'):
funcList.append(lambda m=m: callback(m))
на самом деле в Python нет переменных в классическом смысле, а есть только имена, связанные ссылками на соответствующий объект. Даже функции в Python являются своего рода объектами, и лямбда-выражения не являются исключением из правила :)
Переменная 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
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 :)
Да, это проблема области видимости, она привязывается к внешнему 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))
Не имеет прямого отношения к рассматриваемому вопросу, но тем не менее является бесценной мудростью: Объекты Python Фредрика Лунда.