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

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

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

Ответ 1

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

a.py содержит

print foo

b.py содержит

import __builtin__
__builtin__.foo = 1
import a

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

var_name = "my_useful_string"

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

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

import my_globals
print my_globals.foo

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

Ответ 7

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

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

Ответ 8

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

import __builtin__
__builtin__.foo = 'some-value'

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

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

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

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

Ответ 9

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

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

Например:

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

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

Ответ 10

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

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.

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

Ответ 11

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

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

Ответ 12

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

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

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

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

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 и т.д. или путем определения нескольких классов типа маршрутизатора в одном файле. Последнее немного более аккуратное, поэтому, возможно, представляет собой предельное преимущество для использования механизма переменной класса.