Python, петли и замыкания - PullRequest
       19

Python, петли и замыкания

0 голосов
/ 21 января 2019

Я довольно опытный программист на C / C ++ (и, в некоторой степени, на Java). Я изучаю Python, но я сбит с толку некоторыми странными (для моего фона) поведениями языка.

Я изучаю вложенные функции и замыкания (читаю "Learning Python", который кажется мне действительно хорошим источником).

Я понимаю, что если при вызове созданной функции я вкладываю def в цикл for, он ищет последнее значение переменной перехваченного цикла (как он захватывает по ссылке, как сказал бы программист C ++)

funcs = []
for i in range(4):
    def f():
        print(i)
    funcs.append(f)

и запуск программы, результат

>>> for f in funcs:
      f()


3
3
3
3

Теперь я обернулся вокруг этого, когда наткнулся на это (как мне кажется) несоответствие: если я сделаю

for i in range(4):
  funcs[i]()


0
1
2
3

больше сбивает с толку, если я сделаю

>>> i = 2
>>> funcs[i]()

2

и теперь все функции в списке возвращают 2:

for f in funcs:
  f()


2
2
2
2

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

Ответы [ 3 ]

0 голосов
/ 21 января 2019

Сначала создается список из четырех функций.

funcs = []
for i in range(4):
  def f():
    print(i)
  funcs.append(f)

Каждая из этих функций ищет значение i и затем печатает его.

Это перебирает список функций и вызывает каждую из них:

>>> for f in funcs:
      f()

Как указано выше, эти функции ищут i, что в настоящий момент равно 3 из-за цикла for i in range(4), который завершился ранее, поэтому вы получаете четыре распечатки по 3.

Теперь вы снова выполняете цикл, используя i в качестве переменной цикла:

for i in range(4):
  funcs[i]()


0
1
2
3

Первый раз в цикле, i равен 0, поэтому, когда функция ищет i, она получает 0, а затем печатает это. Затем он меняется на 1, затем 2, затем 3.

Следующий код просто меняет i еще одним способом и вызывает функцию:

>>> i = 2
>>> funcs[i]()

2

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

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

funcs = []
for i in range(4):
  def f(num=i):
    print(num)
  funcs.append(f)
0 голосов
/ 21 января 2019

Здесь нет несогласованности. Значение i в f() зависит от значения i из родительской области. После выполнения первого for i in range(4) i будет иметь значение последнего элемента в range, которое равно 3, и, следовательно, все последующие вызовы f() будут печатать 3

Если вы запустите

for i in range(4):
  funcs[i]()

вы переопределяете значение i на каждом шаге итерации, и поэтому вы получаете 0,1,2,3 в качестве значений, напечатанных f. Делая

for x in range(4):
   funcs[x]()

не повлияет на значение i, поэтому вы получите 3 в качестве значения i во всех вызовах функций

0 голосов
/ 21 января 2019

Ваши функции

def f():
    print(i)

выводят текущее значение i.

Если вы пишете

for i in range(4):
    funcs[i]()

, тогда i устанавливается на 0,1,2,3 по мере прохождения цикла.Вот что означает for i in range(4).

Если вы напишите

for f in funcs:
    f()

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

...