Динамически установить локальную переменную
Как вы динамически устанавливаете локальную переменную в Python?
(где имя переменной является динамическим)
ОБНОВЛЕНИЕ: я знаю, что это не хорошая практика, и замечания являются законными, но это не делает это плохим вопросом, только более теоретическим - я не понимаю, почему это оправдывает отрицательные голоса.
7 ответов
В отличие от других уже опубликованных ответов, вы не можете изменить locals()
и ожидайте, что это сработает.
>>> def foo():
lcl = locals()
lcl['xyz'] = 42
print(xyz)
>>> foo()
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
foo()
File "<pyshell#5>", line 4, in foo
print(xyz)
NameError: global name 'xyz' is not defined
Изменение locals()
не определено Вне функции, когда locals()
а также globals()
это же будет работать; внутри функции это обычно не работает.
Используйте словарь или установите атрибут для объекта:
d = {}
d['xyz'] = 42
print(d['xyz'])
или, если вы предпочитаете, используйте класс:
class C: pass
obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)
Редактирование: доступ к переменным в пространствах имен, которые не являются функциями (например, модули, определения классов, экземпляры), обычно осуществляется с помощью поиска по словарю (как указывает Свен в комментариях, существуют исключения, например, классы, которые определяют __slots__
). Локальные функции могут быть оптимизированы для скорости, потому что компилятор (как правило) заранее знает все имена, так что нет словаря, пока вы не вызовете locals()
,
В C реализация Python locals()
(вызывается из функции) создает обычный словарь, инициализированный из текущих значений локальных переменных. Внутри каждой функции любое количество вызовов locals()
будет возвращать тот же словарь, но каждый вызов locals()
обновит его текущими значениями локальных переменных. Это может создать впечатление, что присвоение элементам словаря игнорируется (я изначально писал, что это так). Изменения существующих ключей в словаре, возвращенные из locals()
поэтому только последний до следующего звонка locals()
в том же объеме.
В IronPython все работает немного по-другому. Любая функция, которая вызывает locals()
внутри он использует словарь для своих локальных переменных, поэтому присвоения локальным переменным изменяют словарь, а присвоения словарю изменяют переменные, НО это только в том случае, если вы явно вызываете locals()
под этим именем. Если вы связываете другое имя с locals
функция в IronPython, затем вызывая ее, дает вам локальные переменные для области видимости, где имя было связано, и нет способа получить доступ к локальным функциям через него:
>>> def foo():
... abc = 123
... lcl = zzz()
... lcl['abc'] = 456
... deF = 789
... print(abc)
... print(zzz())
... print(lcl)
...
>>> zzz =locals
>>> foo()
123
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
>>>
Все может измениться в любое время. Единственное, что гарантировано, это то, что вы не можете зависеть от результатов присвоения словарю, возвращенному locals()
,
Другие предложили назначить locals()
, Это не будет работать внутри функции, где местные жители получают доступ с помощью LOAD_FAST
код операции, если у вас нет exec
утверждение где-то в функции. Чтобы поддержать это утверждение, которое может создавать новые переменные, которые не известны во время компиляции, Python затем вынужден обращаться к локальным переменным по имени внутри функции, поэтому пишем locals()
работает. exec
может быть вне пути кода, который выполняется.
def func(varname):
locals()[varname] = 42
return answer # only works if we passed in "answer" for varname
exec "" # never executed
func("answer")
>>> 42
Примечание: это работает только в Python 2.x. Они покончили с этой глупостью в Python 3, и другие реализации (Jython, IronPython и т. Д.) Могут также не поддерживать ее.
Это плохая идея. Как вы получите доступ к переменным, если не знаете их имени? От locals()[xxx]
наверное. Так почему бы не использовать собственный словарь, а не загрязнять locals()
(и воспользоваться возможностью перезаписи переменной, которая действительно нужна вашей функции)?
(Просто быстрое примечание для других googlin')
Хорошо, так что изменение locals()
это не путь (при изменении globals()
должен работать). В это время, exec
может быть, но это мучительно медленно, поэтому, как и с регулярными выражениями, мы можем захотеть compile()
это первое:
# var0 = 0; var1 = 1; var2 = 2
code_text = '\n'.join( "var%d = %d" % (n, n) for n in xrange(3) )
filename = ''
code_chunk = compile( code_text, filename, 'exec' )
# now later we can use exec:
exec code_chunk # executes in the current context
Вы можете изменить locals()
непосредственно:
locals()['foo'] = 'bar'
Но лучшим способом было бы иметь некоторый dict, который содержит все ваши динамические имена переменных как ключи словаря:
d = {}
for some in thing:
d[some] = 'whatever'
Я провел последние... пару часов, наверное, пытаясь взломать отсутствие закрытия функций, и я придумал это, что может помочь:
common_data = ...stuff...
def process(record):
...logic...
def op():
for fing in op.func_dict: # Key line number 1
exec(fing + " = op.func_dict[fing]") # Key line number 2
while common_data.still_recieving:
with common_data._lock:
if common_data.record_available:
process(common_data.oldest_record)
time.sleep(1.0)
op.func_dict.update(locals()) # Key line number 3
threading.Thread(target = op).start()
...
Это довольно сложный пример, но если много местных жителей или вы все еще в процессе создания прототипа, этот шаблон становится полезным. В основном я был просто горький о том, что все хранилища данных реплицируются или перемещаются для обработки делегатов обратного вызова и т. Д.
Вы можете использовать локальный словарь и поместить все динамические привязки как элементы в словарь. Затем, зная имя такой "динамической переменной", вы можете использовать имя в качестве ключа, чтобы получить ее значение.
Допустим, у нас есть следующий словарь:
DictionaryA = {'No Rating': ['Hobbit', 'Movie C', 'Movie G'],
'Forget It': ['Avenger', 'Movie B'],
'Must See': ['Children of Men', 'Skyfall', 'Movie F'],
'3': ['X-Men', 'Movie D'],
'2': ['Captain America', 'Movie E'],
'4': ['Transformers', 'Movie A']}
Я хочу создать новые словари, как показано ниже:
NewDictionary1 = {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}
NewDictionary2 = {'Forget It': ['Avenger', 'Movie B']}
NewDictionary3 = {'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
вкладчик:
dics = [{k:v} for k,v in DictionaryA.iteritems()]
будет выведен на:
[{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}, {'Forget It': ['Avenger', 'Movie B']}, {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}, {'3': ['X-Men', 'Movie D']}, {'2': ['Captain America', 'Movie E']}, {'4': ['Transformers', 'Movie A']}]
Но для точного объявления переменных мы могли бы пойти с:
>>> i=0
>>> lcl = locals()
>>> for key,val in DictionaryA.iteritems():
lcl["Dict" + str(i)] = {key:val}
i += 1
Как видно первые 3 Dict
переменные:
>>> Dict0
{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
>>> Dict1
{'Forget It': ['Avenger', 'Movie B']}
>>> Dict2
{'No Rating': ['Hobbit', 'Movie C', 'Movie G']}
Как уже упоминалось другими, если вы хотите поместить его в функцию, вы должны добавить его в globals()
:
>>> glb = globals()
>>> for key,val in DictionaryA.iteritems():
glb["Dict" + str(i)] = {key:val}
i += 1