Как мне вызвать setattr () в текущем модуле?

143

Что мне передать в качестве первого параметра objectфункции setattr(object, name, value), чтобы установить переменные в текущем модуле?

Например:

setattr(object, "SOME_CONSTANT", 42);

дает тот же эффект, что и:

SOME_CONSTANT = 42

внутри модуля, содержащего эти строки (с правильными object).

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

Мэтт Джойнер
источник

Ответы:

225
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

или без использования setattr(что нарушает букву вопроса, но удовлетворяет тем же практическим целям ;-):

globals()[name] = value

Примечание : в области модуля последнее эквивалентно:

vars()[name] = value

который немного более краток, но не работает изнутри функции ( vars()дает переменные области, в которой он вызван: переменные модуля при вызове в глобальной области, а затем можно использовать его R / W, но функция переменные при вызове в функции, а затем они должны обрабатываться как R / O - онлайн-документы Python могут немного запутать это конкретное различие).

Алекс Мартелли
источник
9
Документы предупреждают об изменении vars (). docs.python.org/library/functions.html#vars . Когда это можно делать?
unutbu
2
@ ~ unutbu, я бы не сказал, что это «нормально», но он будет работать, когда вы вызываете vars()на уровне модуля, а не внутри функции.
Майк Грэм
4
vars()эквивалентно globals()области видимости модуля (и поэтому возвращает истинный, изменяемый словарь), но locals()области видимости функции (и поэтому возвращает псевдодикт, который нельзя изменять). Я использую vars()в области модуля, так как он сохраняет 3 символа, один слог, по сравнению с его синонимом в этой области globals();-)
Алекс Мартелли
14
Да, это разрушило бы самую важную оптимизацию, которую выполняет компилятор Python: локальные переменные функции не хранятся в dict, они находятся в узком векторе значений, и каждый доступ к локальной переменной использует индекс в этом векторе, а не поиск имени. Чтобы победить оптимизацию, заставив dict, который вы хотите, существовать, запустите функцию с exec '': установите функцию с парой существенных циклов в каждую сторону, и вы увидите важность этой базовой оптимизации для производительности Python.
Алекс Мартелли
3
@msw, я думаю, вы забыли "практичность лучше чистоты" ;-).
Alex Martelli
7

В Python 3.7 вы сможете использовать __getattr__на уровне модуля ( соответствующий ответ ).

Согласно PEP 562 :

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
Трей Ханнер
источник
6

Если вы должны установить переменные области модуля изнутри модуля, что не так global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

таким образом:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']
MSW
источник
1
Да, я всегда (где «всегда» определяется как «последние несколько месяцев, когда я изучаю Python») находил это global but not reallyзаявление озадачивающим. Я полагаю, что это может быть исторический реликт, предшествующий пространствам имен модулей.
msw
1
Исходный вопрос заключается в том, чтобы задать атрибут, имя которого задается строкой (то же самое, что я сейчас искал), поэтому это не поможет.
Курт
-1
  1. Ты бы не стал. Ты бы сделалglobals()["SOME_CONSTANT"] = 42
  2. Ты бы не стал. Вы должны хранить динамически сгенерированный контент не в модуле.
Майк Грэм
источник
Да, SOME_CONSTANTвычисление во время выполнения не совсем постоянное. А если globals()он недоступен для вас, значит, вы должны обратиться к другому модулю, чтобы изменить его атрибуты; это обязательно заставит людей задуматься.
msw
3
Константы и изменяемые исключают друг друга. Постоянные и динамически генерируемые - нет. Значения, которые я генерирую, всегда одни и те же и определяются на основе дополнительных «констант», чтобы сэкономить на арифметике и вводе с моей стороны.
Мэтт Джойнер