2016-09-09 1 views
0

У меня есть функция и вы хотите получить переданные ей аргументы. Я пытался добиться этого с помощью locals(), но я в конечном итоге получить ключи от locals(), а не значения пара:`Для x в locals():` - RuntimeError: Почему locals() меняет размер?

>>>def print_params(i,j,k): 
    for x in locals(): 
     print(x) 
>>>print_params('a','b','c') 
j 
i 
k  #<--I want to get back 'a','b','c'. 

Я хотел бы получить значения вместо ключей от locals().

С нормальным Словаре, я знаю, что могу это сделать:

>>> m = { 
... 'a' : 'b', 
... 'y' : 'z'} 
>>> m 
{'a': 'b', 'y': 'z'} 
>>> for x in m: 
...  print(x) 
... 
a  # <--Keys 
y 
>>> for x in m: 
...  print(m[x]) 
... 
b  # <--Values 
z 

Но, если бы я попробовать это с locals(), он создает RuntimeError.

>>> def print_args(i,j,k): 
...  for x in locals(): 
...   print(locals()[x]) 
... 
>>> print_args('a','b','c') 
b       # <--Got one! 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in y 
RuntimeError: dictionary changed size during iteration 

Может кто-нибудь объяснить, что происходит? Из единственного значения, которое я получил, чтобы напечатать (до того, как он выйдет из строя), я вижу, что ошибка возникает, когда цикл пытается перебрать следующий элемент из locals(); то, когда дикт был изменен, он теряет свое место. Я не могу понять, почему размер словаря должен измениться. Я предполагаю, что на каждом проходе через цикл for переменная x уничтожается, а затем воссоздается для следующего прохода через цикл. Однако это кажется странным, так как я думаю, что было бы более эффективно поддерживать x «живым» и просто переназначить его на новое значение на каждой итерации.

В конечном счете, я думаю, я мог бы просто сделать local_args = locals(), но мне все еще интересно, что именно происходит, что мешает мне просто перебирать locals(). Я не уверен, что я прав, когда x уничтожается/воссоздается; если я, я должен задаться вопросом, почему это так, а если нет, то мне еще любопытно, что происходит.

+2

Потому что 'x' - это новая переменная: это, следовательно, новый элемент в словаре локальных переменных. – zondo

+0

«Но, если я попробую это с помощью locals()« Нет, вы пробовали что-то тонко другое. –

+1

Вы не должны использовать 'locals()'. Почему ваша функция не может быть 'def print_params (* i):', а затем 'for x in i: print (x)' или просто 'print (* i)'? Он даже сохранил исходный порядок аргументов, когда вы их передали. – TigerhawkT3

ответ

1

Can someone explain what's happening? From the single value I got to print (before it errors out), I can see that the error arises when the loop attempts to iterate to the next element of locals(); then, as the dict has been modified, it loses its place. I can't make sense of why the dictionary size should change, though. I'm inferring that, on each pass through the for loop, the variable x is destroyed, then re-created for the next pass through the loop. However, this seems odd, since I would think it'd be more efficient to keep x "alive" and just re-assign it to the new value on each iteration.

Переменная x создается только один раз, но он не будет создан, пока вы не начнете итерации более locals(), потому что исходный итерабельный цикл for оценивается до того, как все будет присвоено переменной цикла. Однако сообщение об ошибке выражается самим dict (точнее, его итератором): он «замечает», когда что-то пытается его повторить, и он изменился со времени его последней итерации.Так что же происходит:

  1. locals() оценивается и дает Dict
  2. петля for создает итератор над Dict и получает первый элемент
  3. новый локальный x создан и установлен, что первый элемент
  4. на следующей итерации цикла, цикл for пытается получить еще один элемент из ДИКТ итератора
  5. в ДИКТ итератор уведомления о том, что лежащий в основе ДИКТ имеет новый элемент (x) с последней итерации, поэтому он вызывает ошибку.

Простым обходным путем является создание переменной x с некоторым количеством фиктивных значений перед циклом. Тогда это будет уже существовать, прежде чем когда-либо доступ к locals(), и будет не создание новых переменных во время цикла:

def print_args(i,j,k): 
    x = None 
    for x in locals(): 
     print(locals()[x]) 

>>> print_args('a', 'b', 'c') 
a 
x 
c 
b 

Обратите внимание, конечно, что вы также получите x себя во время этой итерации.

Как уже упоминалось в комментариях, не совсем понятно, почему вы это делаете. locals() - довольно скользкий зверь, и, как правило, с определенной практической задачей, которую вы пытаетесь выполнить, есть лучший способ сделать это, не используя locals().

+0

Я понимаю, что теперь, основываясь на том, что говорят другие. Я только что обнаружил 'locals()' существует, потому что я пытался вернуть аргументы функции, и это было предложено (... где-то). Я думаю, что лучшее упомянутое решение использует 'function (* i)', что кажется намного лучше, учитывая, что я получаю только аргументы и ничего больше. –

0

Когда вы звоните for x in locals(), оценивается locals(), тогда его первый элемент хранится в x. x не был определен ранее, поэтому он добавляется в словарь locals во время первого цикла, а второй цикл отмечает изменение.

Один из способов workarounding это скопировать locals словарь заранее, как вы уже упоминали:

def print_args(i,j,k): 
    local_args = dict(locals()) 
    for x in local_args: 
     print(local_args[x]) 

print_args('a','b','c')