2009-09-20 2 views
11

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

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

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

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

Use Case

Рассмотрим:

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

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

+0

Ссылка на соответствующую документацию: https://docs.python.org/2/library/functions.html#locals –

+0

почему 'test. Зависимости = ["a", "b", "c", "d", "e", "f"] 'работают, а затем украшают присваивание, которое я написал выше, к вашей функции' test() '? –

+0

вам удалось обновить/изменить местные жители или нет? –

ответ

11

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

+0

Я думаю, что это довольно убедительно, что это просто невозможно – Casebash

+1

@Casebash, возможно, это возможно, ему просто нужны байтовые коды или Python 2.x – Unknown

+0

Хорошо, я оставлю этот вопрос нерешенным на данный момент – Casebash

-1

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

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

Это точно так же, как locals(); 'inspect.currentframe(). f_locals - locals()' is true. –

+1

Это не ** полностью ** неправильно, но оно работает только тогда, когда кадр является самым верхним, т. Е. Глобальным. Он не будет работать в локальных областях. – bendtherules

3

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

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

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

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

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

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

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

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

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

5

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

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

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

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) 

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

+0

только то, что я думал; вы также можете динамически создавать функцию; см. справку (types.FunctionType) – gatoatigrado

+4

Это интересная идея. Однако есть много приложений, в которых словарь фактически содержит много других переменных (которые не нужны 'aFunction()'), что делает текущее определение 'aFunction()' break. Полезным обобщением является: aFunction (a, b, c, d, e, f, ** kwargs) '. – EOL

+0

@EOL: дополнительные переменные параметра вызывают разрыв функции? Это трудно себе представить. Несколько дополнительных переменных должны быть - просто - просто переменными. Функция, которая ломается из-за нескольких дополнительных переменных, имеет очень плохой дизайн. Было бы лучше исправить эту функцию. –

1

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

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, прежде чем положить его в местных():

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

Смежные вопросы