2015-04-21 2 views
2

У меня есть этот пример кода, где я ожидал, что он напечатает текущую переменную test.x, но когда я использую цикл for для определения списка ссылок на функции, я не получаю то, m ожидая ([1,1] и [0,0]). Я получаю то, что ожидаю, когда вместо этого использую прокомментированные строки ([0,1] и [1,0]). Я понимаю, что есть более простые способы сделать это, но для моей программы мне нужно, чтобы это было так, но я хочу определить объект правил в цикле for, а не определять каждый элемент в строке, потому что я не знаю, как большой объект правил будет. Спасибо за любую помощь (Python 2,7)Python for loop инициализация ссылки на функцию

class TestClass: 
    def __init__(self): 
     self.x = list([0, 1]) 
    def get_value(self, i): 
     return self.x[i] 

test = TestClass() 
rules = list([None, None]) 
for a in range(2): 
    rules[a] = lambda t: test.get_value(a) 
#rules[0] = lambda t: test.get_value(0) 
#rules[1] = lambda t: test.get_value(1) 

print(rules[0](0), rules[1](0)) 
test.x[0] = 1 
test.x[1] = 0 
print(rules[0](0), rules[1](0)) 
+1

Зачем вам копировать. 0 и 1? –

+0

Я думаю, что проблема заключается в правилах [0], а правила [1] оба вызываются test.get_value (1), и я думал, что copy.copy может помочь, но это не –

+0

Вы отметили вопрос python 2.7, но ваш код появляется be python 3 –

ответ

2

Проблема может быть показано более сжато следующим образом:

>>> rules = list([None, None]) 
>>> for a in range(2): 
... rules[a] = lambda t: a 
... 
>>> rules[0](0) 
1 
>>> rules[0](1) 
1 
>>> rules[1](0) 
1 
>>> rules[1](1) 
1 

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

Это известно как «позднесвязывающие затворы» и обсуждается в Python Guide here.

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

>>> from functools import partial 
>>> for a in range(2): 
... def get(t,x): return x 
... rules[a] = partial(get,x=a) 
... 
>>> rules[0](0) 
0 
>>> rules[0](1) 
0 
>>> rules[1](0) 
1 
>>> rules[1](1) 
1 

Более простой способ достижения того же эффекта:

>>> for a in range(2): 
... rules[a] = lambda t,a=a: a 

Как показано в связанном руководстве Python, вы можете также использовать список понимание для упрощения кода немного:

rules = [lambda t,a=a: a for a in range(2)] 
+0

Спасибо, что работает. Хотя его можно упростить с помощью частичного (лямбда t, x: x, x = a) –