Есть ли способ изменить словарь локальных жителей?

locals - это встроенная функция, которая возвращает словарь локальных значений. В документации сказано:

Предупреждение

Содержание этого словаря не должно быть изменено; изменения могут не повлиять на значения локальных переменных, используемых интерпретатором.

К сожалению, exec имеет ту же проблему в Python 3.0. Есть ли способ обойти это?

Случай использования

Рассматривать:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

зависящий хранит строки, предоставленные в его аргументах в списке test.dependences. Эти строки являются ключами в словаре d. Я хотел бы иметь возможность написать put_into_locals чтобы мы могли извлечь значения из d и поместить их в put_into_locals. Это возможно?

Ответ 1

Я только что протестировал exec и работает в Python 2.6.2

>>> def test():
...     exec "a = 5"
...     print a
...
>>> test()
5

Если вы используете Python 3.x, он больше не работает, потому что локали оптимизируются как массив во время выполнения, вместо использования словаря.

Когда Python обнаруживает "оператор exec", он заставит Python переключать локальное хранилище из массива в словарь. Однако поскольку "exec" является функцией в Python 3.x, компилятор не может сделать это различие, поскольку пользователь мог бы сделать что-то вроде "exec = 123".

http://bugs.python.org/issue4831

Чтобы изменить локали функции на летать невозможно без несколько последствий: обычно, локальные функции не сохраняются в словарь, но массив, чей индексы определяются во время компиляции из известных мест. Это сталкивается по крайней мере, с новыми местными жителями, добавленными Отв. Старая инструкция exec обошли это, потому что компилятор знал, что если exec без globals/locals args произошли в функции, что пространство имен будет "неоптимизировано", то есть не использовать локальный массив. Поскольку exec() теперь является нормальная функция, компилятор не знать, к чему может быть привязан "exec", и поэтому не может быть обработано специально.

Ответ 2

Локальные переменные изменяются операторами присваивания.

Если у вас есть словарные ключи, которые являются строками, не делайте их локальными переменными - просто используйте их в качестве словарных клавиш.

Если вы абсолютно должны иметь локальные переменные, сделайте это.

def aFunction( a, b, c, d, e, f ):
    # use a, b, c, d, e and f as local variables

aFunction( **someDictWithKeys_a_b_c_d_e_f )

Это будет заполнять некоторые локальные переменные из вашего словаря, не делая ничего волшебного.

Ответ 3

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

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

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

См. этот вопрос для одного возможного решения, но это серьезный взлом, и вы действительно не хотите этого делать.

Обратите внимание, что есть основная проблема с вашим примером кода:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

"test.dependencies" не относится к "f.dependencies", где f - текущая функция; он ссылается на фактическое глобальное значение "тест". Это означает, что если вы используете более одного декоратора:

@memoize
@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

он больше не будет работать, так как "test" - memoize wrapped function, а не зависит от. Python действительно нуждается в способе ссылаться на "исполняемую в настоящее время функцию" (и класс).

Ответ 4

Я бы сохранил его в переменной:

refs    = locals()
def set_pets():
    global refs
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
    for i in range(len(animals)):
        refs['pet_0%s' % i] = animals[i]

set_pets()
refs['pet_05']='bird'
print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05 )
>> dog fish monkey cat fox bird

И если вы хотите протестировать свой dict, прежде чем поместить его в locals():

def set_pets():
    global refs
    sandbox = {}
    animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
    for i in range(len(animals)):
        sandbox['pet_0%s' % i] = animals[i]
    # Test sandboxed dict here
    refs.update( sandbox )

Python 3.6.1 на MacOS Sierra

Ответ 5

Я не уверен, что он подвержен тем же ограничениям, но вы можете получить прямую ссылку на текущий кадр (и оттуда - словарь локальных переменных) через модуль проверки:

>>> import inspect
>>> inspect.currentframe().f_locals['foo'] = 'bar'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect']
>>> foo
'bar'