2010-01-10 2 views
3

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

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

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

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

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

Редактировать: Для дополнительного решения - отметьте this ответ.


Edit2:

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

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

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

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

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

ответ

4

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

Например, в 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 

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

+0

У моего решения от 2-го редактирования (в вопросе) есть достаточно низкий уровень магии? ;) По-прежнему существует проблема с отсутствием объяснения. – gorsky

+1

@gorsky, ваше второе решение (которое является достаточно явным и не магическим) не делает ничего даже ** близко ** к тому, что вы говорите в своем Q, которое вы хотите сделать: оно всегда изменяет var в главном модуле , ** не ** в вызывающем модуле! Просто попробуйте с помощью модулей 'a' и' b', которые я покажу в своем ответе, если это не совсем очевидно для вас. –

+0

Вы правы - я протестировал это неправильно: называть его как 'python a.py' делает' a.py' основным модулем, тем самым работая так, как я хотел. Неправильное предположение, спасибо, что помогли мне это сделать. – gorsky

0

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

+0

Я не могу этого сделать, потому что не знаю, в каком модуле будет вызываться моя функция (несколько модулей с переменной 'urlpatterns'). – gorsky

4

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

+0

Ты прав, я боюсь, но я подожду магов и посмотрю их трюки;) – gorsky

+0

+1 для Дзэн. –

2

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

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

Он работает, но он слишком волшебный. Спасибо, однако, я передам это явно. Я просвещен дзеном Питона. – gorsky

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