Динамическая локальная переменная

Как вы динамически устанавливаете локальную переменную в Python?

(где имя переменной является динамическим)

ОБНОВЛЕНИЕ: Я знаю, что это не очень хорошая практика, и замечания законны, но это не делает его плохим вопросом, а более теоретическим - я не почему это оправдывает downvotes.

Ответ 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() - undefined. Вне функции, когда locals() и globals() будут одинаковыми, она будет работать; внутри функции обычно не работает.

Используйте словарь или задайте атрибут для объекта:

d = {}
d['xyz'] = 42
print(d['xyz'])

или, если хотите, используйте класс:

class C: pass

obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)

Edit: Доступ к переменным в пространствах имен, которые не являются функциями (так что модули, определения классов, экземпляры) обычно выполняются поиском по словарю (как указывает Свен в комментариях, есть исключения, например классы, которые определяют __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().

Ответ 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

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

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()

...

Это довольно тяжелый/надуманный пример, но если есть много локальных жителей, или вы все еще в процессе прототипирования, этот шаблон становится полезным. В основном я был просто ожесточен по поводу того, что все хранилища данных реплицируются или перемещаются для обработки делегатов обратного вызова и т.д.

Ответ 4

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

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

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

d = {}
for some in thing:
    d[some] = 'whatever'

Ответ 5

Вы можете использовать локальный словарь и поместить все динамические привязки в качестве элементов в словарь. Затем, зная имя такой "динамической переменной", вы можете использовать это имя в качестве ключа, чтобы получить его значение.

Ответ 6

(Просто быстрая заметка для других 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

Ответ 7

Скажем, у нас есть словарь ниже:

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']}

a oneliner:

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