Как получить переменную модуля в функции из другого модуля? - PullRequest
3 голосов
/ 10 января 2010

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

# mod1.py
mod_var = 1
modify_var()
# mod_var modified
print mod_var

Проблема в том, что я не могу ссылаться на переменную mod1.mod_var, потому что я хочу использовать вспомогательную функцию во многих модулях (сам помощник будет определен в другом модуле); он должен динамически «выбирать» mod_var из окружающего вызывающего контекста / области видимости.

Возможно ли это? Как это получить?

Мой вариант использования - улучшить определение URL -> отображение представления в Django. Эти определения распределены по многим подмодулям, которые определяют urlpatterns переменную уровня модуля. Вспомогательная функция должна выбрать эту переменную из вызывающего ее модуля и изменить ее. Было бы здорово избежать явной передачи его в качестве аргумента.

Изменить: Для дополнительного решения - отметьте этот ответ.


Edit2:

Неправильное решение ниже! (оставлено для ссылок в комментариях)

Недавно я нашел другое решение (наименее волшебное на мой взгляд;)) modify_var() функция может быть реализована так:

def modify_var():
    calling_module = __import__("__main__")
    calling_module.mod_var = 42

Тем не менее, потенциальная прибыль спорна.

Модуль

unittest использует эту технику в своем методе main.

Ответы [ 4 ]

4 голосов
/ 10 января 2010

Это действительно плохая, ужасная и ужасная идея, которая приведет к будущим кошмарам обслуживания. Тем не менее, Python предлагает «достаточно веревки, чтобы выстрелить себе в ногу», если вы действительно настаиваете: инструменты самоанализа и метапрограммирования, которые в основном предназначены для отладки, но могут быть использованы для выполнения непродуманной задачи, которую вы так отчаянно жаждете. 1001 *

Например, в evil.py:

import inspect

def modify_var():
  callersframe = inspect.stack()[1][0]
  callersglobals = callersframe.f_globals
  if 'mod_var' not in callersglobals:
    raise ValueError, 'calling module has no "mod_var"!'
  callersglobals['mod_var'] += 1

Теперь скажите, что у вас есть два модуля, a.py:

import evil

mod_var = 23
evil.modify_var()
print 'a mod_var now:', mod_var

и b.py:

import evil

mod_var = 100
evil.modify_var()
print 'b mod_var now:', mod_var

Вы могли бы сделать:

$ python -c'import a; import b'
a mod_var now: 24
b mod_var now: 101

Тем не менее, сохранение такого рода уловок черной магии в будущем будет головной болью, поэтому я настоятельно рекомендую не делать что-то таким образом.

4 голосов
/ 10 января 2010

То, что вы хотите сделать, звучит как слишком много магии. Пройдите в urlpatterns и покончите с этим. Явное лучше, чем неявное.

2 голосов
/ 10 января 2010

ОК, вот волшебство, но опять же, я рекомендую не использовать его:

import sys

def modify_var():
    """Mysteriously change `mod_var` in the caller's context."""
    f = sys._getframe(1)
    f.f_locals['mod_var'] += " (modified)"

mod_var = "Hello"
modify_var()
print mod_var

печать:

Hello (modified)

В качестве еще одного предупреждения против этого метода: _getframe - это одна из тех функций, которые не предоставляют другие реализации Python, и в документах есть следующее предложение: «Эта функция должна использоваться только для внутренних и специализированных целей»

0 голосов
/ 10 января 2010

Если вы действительно хотите сделать это, вам нужно будет импортировать mod1 либо в другой модуль, либо непосредственно в функцию, а затем изменить его в этом импорте. Но не делай этого; опытные программисты будут указывать и смеяться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...