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

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

Например:

setattr(object, "SOME_CONSTANT", 42);

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

SOME_CONSTANT = 42

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

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


person Matt Joiner    schedule 29.05.2010    source источник


Ответы (4)


import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

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

globals()[name] = value

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

vars()[name] = value

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

person Alex Martelli    schedule 29.05.2010
comment
Документы предупреждают об изменении vars (). docs.python.org/library/functions.html#vars. Когда это можно делать? - person unutbu; 29.05.2010
comment
@ ~ unutbu, я бы не сказал, что это нормально, но он будет работать, когда вы вызываете vars() в области видимости на уровне модуля, а не внутри функции. - person Mike Graham; 29.05.2010
comment
Спасибо за информацию, @Mike. Есть ли простой способ понять, почему vars() ведет себя таким образом (нормально на уровне модуля, но не в функциях)? Было ли это ограничение намеренно или что-то еще? - person unutbu; 29.05.2010
comment
vars() эквивалентен globals() в области видимости модуля (и поэтому возвращает истинный, изменяемый dict), но locals() в области функции (и поэтому возвращает псевдодикт, который никогда не будет изменен). Я использую vars() в области модуля, поскольку он сохраняет 3 символа, один слог, по сравнению с его синонимом в этой области globals() ;-) - person Alex Martelli; 29.05.2010
comment
Хорошо, это полезно, @Alex; благодаря. Но почему locals() в области видимости функции должен возвращать псевдодикт, который не подлежит изменению? Было бы что-то не так с выставлением здесь изменяемого дикта? - person unutbu; 29.05.2010
comment
Да, это разрушило бы самую важную оптимизацию, которую выполняет компилятор Python: локальные переменные функции не хранятся в dict, они находятся в узком векторе значений, и каждый доступ к локальной переменной использует индекс в этом векторе, а не поиск по имени. Чтобы обойти оптимизацию, заставив dict, который вы хотите, существовать, запустите функцию с exec '': time функции с парой существенных циклов в каждую сторону, и вы увидите важность этой базовой оптимизации для производительности Python. - person Alex Martelli; 29.05.2010
comment
Хм, есть ли причина, по которой я не могу просто сделать globals()[name] = value, имея в виду, что это всегда словарь текущего модуля, и что vars() эквивалентно globals() в вашем ответе? Это безопасно? - person Matt Joiner; 29.05.2010
comment
@Matt, конечно, это безопасно (vars и globals эквивалентны на верхнем уровне модуля, и если вы находитесь в функции, globals вы все равно хотите использовать для установки переменной уровня модуля). Хотя вы можете использовать setattr, как вы решили задать в этом вопросе, вы также можете получить тот же эффект с рассматриваемым индексированным назначением. И, кстати, вы можете также иметь __getattr__, что ведет себя примерно как модуль (может быть импортирован и т. Д.), Но, я думаю, это еще одна проблема. - person Alex Martelli; 29.05.2010
comment
@Alex Martelli: Поскольку у меня нет возможности отправить вам здесь сообщение, возможно, вы могли бы взглянуть на stackoverflow .com / questions / 2447353 / getattr-on-a-module, который меня очень интересует. - person Matt Joiner; 29.05.2010
comment
Красивое лучше уродливого. Явное лучше, чем неявное. Лучше простое, чем сложное. Читаемость имеет значение. Особых случаев недостаточно, чтобы нарушать правила. Должен быть один - а желательно только один - очевидный способ сделать это. Если реализацию трудно объяснить, это плохая идея. - person msw; 29.05.2010
comment
@msw, я думаю, вы забыли, что практичность важнее чистоты ;-). - person Alex Martelli; 29.05.2010
comment
@Matt, ах, да, решение @Håvard S в этом Q - это то, что я имел в виду (изначально опубликовав его в Поваренной книге, я проголосовал за него, когда увидел его на SO ;-) - что оно не работает после import * Я вижу одну из множества проблем с import *, а не с рассматриваемым решением; что он не работает с пустыми именами (в текущем модуле, как вы говорите) верно, но тогда голые имена настолько сильно отличаются от полных имен, что этого не следует ожидать. - person Alex Martelli; 29.05.2010
comment
Справедливый нюфф, так что практический смысл vars()[name] = value, кроме сохранения 3 символов, одного слога, который делает его достаточно особенным, чтобы нарушать правила, быть неявным, нечитаемым, трудно объяснимым и сильно зависит от знания конкретной реализации. Да, это умно, как и конструкция C i++-++j, и если она работает так, как вы хотите, на вашей платформе, имеет ли значение что-нибудь еще? Обратите внимание, сколько объяснений этот так называемый практический пример дал умному, заинтересованному читателю. - person msw; 29.05.2010
comment
@msw, о, я думал, вы комментируете несколько моих комментариев (о том, что locals() практически не может быть диктовкой), а не один, сделанный 12 часов назад - трудно отследить, когда вы не даете ссылки. В vars нет ничего неявного или нечитабельного, он не особенно умен, он работает одинаково на всех платформах, и единственное, что сбивает его с толку, - это то, что он неверно задокументирован в онлайн-документации. Думаю, я отредактирую ответ, чтобы указать на это, так как это, кажется, действительно заставляет вас уходить с ума ;-). - person Alex Martelli; 29.05.2010
comment
@matt: предполагая, что ваш вопрос честный - я новичок в Python, и меня привлекли принципы PEP 20 Zen of Python, которые делают хорошо написанный код Python почти очевидным тривиальным. Я считаю, что чрезмерное использование __setattr__, по-видимому, очень соблазнительно, но в итоге приводит к тому, что код становится загадочным. Чрезмерное использование метасинтаксических конструкций затрудняет чтение кода именно по тем причинам, по которым препроцессор C в настоящее время избегают из-за прошлых злоупотреблений. - person msw; 30.05.2010
comment
@msw: Хорошо, теперь ваши комментарии имеют смысл. Я просто счастлив, что это работает, и что описание Алекса устранило мою путаницу. Однако я вижу, что наличие и vars(), и globals(), и даже тот факт, что globals() - вводящее в заблуждение имя ... не идеально, но кто считает? - person Matt Joiner; 31.05.2010

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

Согласно PEP 562:

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
person Trey Hunner    schedule 18.03.2018

Если вы должны установить переменные области модуля изнутри модуля, что не так с 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']
person msw    schedule 29.05.2010
comment
Да, я всегда (где всегда определяется как последние несколько месяцев, когда я изучал Python) находил это объявление global but not really озадачивающим. Я полагаю, что это может быть историческая реликвия, предшествующая пространствам имен модулей. - person msw; 29.05.2010
comment
Исходный вопрос заключается в том, как установить атрибут, имя которого задается строкой (то же самое, что я сейчас искал), так что это не поможет. - person Curt; 10.02.2016

  1. Ты бы не стал. Вы бы сделали globals()["SOME_CONSTANT"] = 42
  2. Ты бы не стал. Вы должны хранить динамически сгенерированный контент где-нибудь, кроме модуля.
person Mike Graham    schedule 29.05.2010
comment
Да, SOME_CONSTANT, вычисленное во время выполнения, не совсем константа. А если globals() недоступен для вас, значит, вы должны подключиться к другому модулю, чтобы изменить его атрибуты; это обязательно заставит людей задуматься. - person msw; 29.05.2010
comment
Константы и изменяемые исключают друг друга. Постоянные и динамически генерируемые - нет. Значения, которые я генерирую, всегда одинаковы и определяются на основе дополнительных констант, чтобы сэкономить на арифметике и вводе с моей стороны. - person Matt Joiner; 29.05.2010