Как сделать кросс-модульную переменную? - PullRequest
115 голосов
/ 27 сентября 2008

Переменная __debug__ удобна отчасти потому, что влияет на каждый модуль. Если я хочу создать другую переменную, которая работает таким же образом, как бы я это сделал?

Переменная (давайте будем оригинальной и назовем ее 'foo') не обязательно должна быть действительно глобальной, в том смысле, что если я изменю foo в одном модуле, она будет обновлена ​​в других. Было бы хорошо, если бы я мог установить foo перед импортом других модулей, и тогда они увидели бы то же значение для него.

Ответы [ 12 ]

149 голосов
/ 27 сентября 2008

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

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Тест:

$ python b.py
# -> 1 2

Реальный пример: global_settings.py * 1016 Django (хотя в приложениях Django настройки используются при импорте объекта django.conf.settings).

106 голосов
/ 27 сентября 2008

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

a.py содержит

print foo

b.py содержит

import __builtin__
__builtin__.foo = 1
import a

В результате получается «1».

Редактировать: Модуль __builtin__ доступен в виде локального символа __builtins__ - вот причина расхождений между двумя из этих ответов. Также обратите внимание, что __builtin__ был переименован в builtins в python3.

22 голосов
/ 23 февраля 2013

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

Когда есть только один такой модуль, я называю его "g". В нем я назначаю значения по умолчанию для каждой переменной, которую я намерен рассматривать как глобальную. В каждом модуле, который использует любой из них, я не использую «из g import var», поскольку это приводит только к локальной переменной, которая инициализируется из g только во время импорта. Я делаю большинство ссылок в форме g.var, и "g." служит постоянным напоминанием о том, что я имею дело с переменной, которая потенциально доступна для других модулей.

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

Обратите внимание, что вы также можете иметь несколько таких глобальных модулей, совместно используемых различными подмножествами ваших модулей, чтобы держать вещи немного более жестко под контролем. Причина, по которой я использую короткие имена для своих глобальных модулей, состоит в том, чтобы не перегружать код слишком частыми их появлениями. Имея лишь небольшой опыт, они становятся достаточно мнемоническими, имея всего 1 или 2 символа.

Все еще возможно сделать присвоение, скажем, g.x, когда x еще не было определено в g, и тогда другой модуль может получить доступ к g.x. Однако, хотя переводчик это разрешает, этот подход не так прозрачен, и я его избегаю. Все еще существует возможность случайного создания новой переменной в g в результате опечатки в имени переменной для присваивания. Иногда исследование dir (g) полезно для обнаружения любых неожиданных имен, которые могли возникнуть в результате такой аварии.

22 голосов
/ 27 сентября 2008

Определите модуль (назовите его «globalbaz») и определите переменные внутри него. Все модули, использующие этот «псевдоглобальный», должны импортировать модуль «globalbaz» и обращаться к нему с помощью «globalbaz.var_name»

Это работает независимо от места изменения, вы можете изменить переменную до или после импорта. Импортированный модуль будет использовать последнее значение. (Я проверял это на игрушечном примере)

Для пояснения, globalbaz.py выглядит так:

var_name = "my_useful_string"
9 голосов
/ 17 июля 2010

Вы можете передать глобальные переменные одного модуля другому:

В модуле A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

В модуле B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3
7 голосов
/ 27 сентября 2008

Глобальные переменные обычно плохая идея, но вы можете сделать это, назначив __builtins__:

__builtins__.foo = 'something'
print foo

Кроме того, сами модули являются переменными, к которым вы можете обращаться из любого модуля. Так что если вы определяете модуль с именем my_globals.py:

# my_globals.py
foo = 'something'

Тогда вы также можете использовать это из любого места:

import my_globals
print my_globals.foo

Использование модулей вместо модификации __builtins__, как правило, является более чистым способом создания глобальных переменных такого типа.

5 голосов
/ 12 октября 2010

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

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

3 голосов
/ 31 июля 2015

Я хотел опубликовать ответ, что есть случай, когда переменная не будет найдена.

Циклический импорт может нарушить поведение модуля.

Например:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

На этом примере это должно быть очевидно, но в большой базе кода это может привести к путанице.

2 голосов
/ 27 сентября 2008

Это звучит как изменение пространства имен __builtin__. Для этого:

import __builtin__
__builtin__.foo = 'some-value'

Не используйте __builtins__ напрямую (обратите внимание на дополнительные «s») - очевидно, это может быть словарь или модуль. Спасибо ΤΖΩΤΖΙΟΥ за указание на это, больше можно найти здесь .

Теперь foo доступен для использования везде.

Я не рекомендую делать это вообще, но использовать это до программиста.

Назначение ему должно быть сделано, как указано выше, просто установка foo = 'some-other-value' установит его только в текущем пространстве имен.

1 голос
/ 02 декабря 2013

Я мог бы получить кросс-модульные модифицируемые (или изменяемые ) переменные, используя словарь:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

При запуске test_wait_app_up_fail фактическое время ожидания составляет 3 секунды.

...