Вопрос-Ответ

Dynamically set local variable [duplicate]

Динамически устанавливаемая локальная переменная [дубликат]

Как динамически устанавливать локальную переменную в Python (где имя переменной является динамическим)?

Переведено автоматически
Ответ 1

В отличие от других уже опубликованных ответов, вы не можете изменять 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)

Редактировать:
Доступ к переменным в пространствах имен, которые не являются функциями (таким образом, модули, определения классов, экземпляры), обычно осуществляется путем поиска по словарю (как указывает Sven в комментариях, существуют исключения, например классы, которые определяют __slots__). Локальные значения функций могут быть оптимизированы для повышения скорости, потому что компилятор (обычно) знает все имена заранее, поэтому словаря нет, пока вы не вызовете locals().

В реализации 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().

Ответ 2

Другие предлагали присвоить значение 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() (и не рисковать перезаписью переменной, которая действительно нужна вашей функции)?

Ответ 3

(Просто небольшое замечание для тех, кто гуглит)

Ок, значит, изменять 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
Ответ 4

Вы могли бы изменить locals() напрямую:


locals()['foo'] = 'bar'


Но лучшим способом было бы иметь некоторый dict, который содержит все ваши имена динамических переменных в качестве ключей словаря:

d = {}
for some in thing:
d[some] = 'whatever'
2023-07-09 15:33 python