карринг функции в питоне в цикле - PullRequest
3 голосов
/ 24 июня 2010

Итак, вот некоторый код, который упрощает то, над чем я работал:

vars = {
    'a':'alice',
    'b':'bob',
}
cnames = ['charlie', 'cindy']

commands = []

for c in cnames:
    kwargs = dict(vars)
    kwargs['c'] = c
    print kwargs
    commands.append(lambda:a_function(**kwargs))

print commands

def a_function(a=None, b=None, c=None):
    print a
    print b
    print c

for c in commands:
    print "run for "+ repr(c)
    c()

И вот его вывод:

{'a': 'alice', 'c': 'charlie', 'b': 'bob'}
{'a': 'alice', 'c': 'cindy', 'b': 'bob'}
[<function <lambda> at 0x1001e9a28>, <function <lambda> at 0x1001e9e60>]
run for <function <lambda> at 0x1001e9a28>
alice
bob
cindy
run for <function <lambda> at 0x1001e9e60>
alice
bob
cindy

Я бы ожидал получить Чарли, тогдаСинди, почему Синди отображается дважды?

Ответы [ 2 ]

5 голосов
/ 24 июня 2010

Вы столкнулись с классической проблемой времени привязки, и решение @ Mike является классическим.Хорошей альтернативой является написание функции более высокого порядка:

def makecall(kwargs):
  def callit():
    return a_function(**kwargs)
  return callit

и использование commands.append(makecall(kwargs)) в цикле.Оба решения работают по одному и тому же принципу (путем принудительного раннего связывания путем передачи аргумента - в моем случае простой аргумент, значение по умолчанию для именованного аргумента в @ Mike's);выбор - это просто вопрос стиля или элегантности (я, в то время как я допускаю lambda в сверхпростых случаях, пока вмешивается самое тонкое осложнение, я очень предпочитаю старый добрый def; -).

4 голосов
/ 24 июня 2010

Тело функции не запускается, пока функция не будет вызвана.Когда вы делаете lambda: a_function(**kwargs), kwargs не просматривается, пока вы на самом деле не вызовете функцию.В этот момент он назначается последнему, который вы сделали в цикле.

Единственное решение, которое дает желаемый результат, - это commands.append(lambda kwargs=kwargs: a_function(**kwargs))

...