Есть ли способ изменить словарь местных жителей? - PullRequest
13 голосов
/ 20 сентября 2009

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 и поместить их в локальные данные. Это возможно?

Ответы [ 5 ]

12 голосов
/ 20 сентября 2009

Я только что проверил 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 без глобальные / местные аргументы произошли в функция, это пространство имен будет «неоптимизированный», т.е. не использующий массив местных жителей. Поскольку exec () теперь нормальная функция, компилятор не знать, с чем может быть связан exec, и поэтому не может относиться специально.

7 голосов
/ 20 сентября 2009

Локальные переменные модифицируются операторами присваивания.

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

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

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 голосов
/ 20 сентября 2009

Это невозможно. Я думаю, что это позволит оптимизировать производительность позже. Байт-код Python ссылается на локальных по индексу, а не по имени; если 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, а не зависит от нее Python действительно нуждается в способе ссылки на "выполняемую в данный момент функцию" (и класс).

1 голос
/ 18 июля 2017

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

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

И если вы хотите проверить свой диктат перед тем, как поместить его в localals ():

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

0 голосов
/ 20 сентября 2009

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

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