Как сделать кросс-модульную переменную?

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

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

12 ответов


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

а.пы содержит

print foo

б.пы содержит

import __builtin__
__builtin__.foo = 1
import a

в результате печатается "1".

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


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

а.пы:

var = 1

б.пы:

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

Си.пы:

import a
a.var = 2

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

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

для уточнения, globalbaz.py выглядит так же, как это:

var_name = "my_useful_string"

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

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

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

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

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


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

В Модуле 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

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

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

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

import my_globals
print my_globals.foo

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


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

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


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

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

например:

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

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


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

import __builtin__
__builtin__.foo = 'some-value'

не используйте __builtins__ напрямую (обратите внимание на дополнительные "s") - по-видимому, это может быть словарь или модуль. Благодаря τωωτζιου для указания на это, больше можно найти здесь.

теперь foo можно использовать везде.

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

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


Я использую это для пары встроенных примитивных функций, которые, как я чувствовал, действительно отсутствовали. Одним из примеров является функция find, которая имеет ту же семантику использования, что и filter, map, reduce.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Как только это будет запущено (например, путем импорта рядом с вашей точкой входа), все ваши модули могут использовать find() как будто, очевидно, он был встроен.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

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


я мог бы достичь кросс-модуль изменяемых (или mutable) переменные с помощью словаря:

# 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 секунды.


я задавался вопросом, Можно ли избежать некоторых недостатков использования глобальных переменных (см., например,http://wiki.c2.com/?GlobalVariablesAreBad), используя пространство имен класса, а не глобальное пространство имен/module для передачи значений переменных. Следующий код указывает, что эти два метода по существу идентичны. Существует небольшое преимущество в использовании пространств имен классов, как описано ниже.

следующие фрагменты кода также показывают, что атрибуты или переменные могут быть динамически созданы и удалены в пространствах имен global/module и Class.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

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

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

этот модуль импортирует стену и определяет одну функцию sourcefn, который определяет сообщение и испускает его двумя различными механизмами, один через глобалы и один через функцию маршрутизатора. Обратите внимание, что переменные wall.msg и wall.router.message определяются здесь впервые в соответствующих пространствах имен.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

этот модуль определяет функцию destfn который использует два разных механизма для получения сообщений, излучаемых источником. Это допускает возможность того, что переменная " msg " может не существовать. destfn также удаляет переменные, как только они были показаны.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

этот модуль вызывает ранее определенные функции в последовательности. После первого звонка в dest.destfn переменные wall.msg и wall.router.msg больше не существует.

выход из программы-это:

global: Привет мир!
маршрутизатор: Привет, мир!
global: нет сообщения
маршрутизатор: нет сообщения

вышеуказанные фрагменты кода показывают, что модуль / глобальный и класс / класс переменные механизмы по существу идентичны.

если много переменных, котор нужно делить, то загрязнение пространства имен можно управлять или путем использование нескольких типа стен модулей, например wall1, wall2 etc. или путем определения нескольких классов типа маршрутизатора в одном файле. Последний немного опрятнее, поэтому, возможно, представляет собой маргинальное преимущество для использования механизма переменной класса.