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

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

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
122
0
162 016
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Похоже на изменение пространства имен __builtin__. Сделать это:

import __builtin__
__builtin__.foo = 'some-value'

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

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

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

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

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

tzot 27.09.2008 04:13
Ответ принят как подходящий

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

a.py содержит

print foo

b.py содержит

import __builtin__
__builtin__.foo = 1
import a

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

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

Есть ли причина, по которой вам не нравится эта ситуация?

Software Enthusiastic 05.08.2010 18:32

Во-первых, это разбивает ожидания людей, когда они читают код. «Что это за символ« foo »? Почему я не вижу, где он определен?»

Curt Hagenlocher 06.08.2010 09:05

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

intuited 12.10.2010 05:20

Это хорошее решение для таких вещей, как совместное использование db-соединения с импортированными модулями. В качестве проверки работоспособности я убеждаюсь, что импортированный модуль утверждает hasattr(__builtin__, "foo").

Mike Ellis 18.01.2012 02:52

Я бы рекомендовал удалить импортный __builtin__ и просто использовать __builtins__

B T 16.10.2012 03:58

Это также хорошее решение, если вы хотите создать какой-то уровень совместимости между питонами - например, вы можете использовать type для создания нового типа и переопределения функции type таким образом, чтобы в Python <3 он автоматически наследовался от объекта.

Wojciech Danilo 03.01.2013 15:00

Ноно, это именно то, от чего я пытаюсь уйти! Мне нужна указанная переменная с пространством имен, чтобы не касался моего встроенные, чтобы он мог оставаться нетронутым от этого мусора.

ThorSummoner 14.10.2015 09:21

Для тех, кто читает этот ответ: НЕ! ДЕЛАТЬ ! ЭТО ! На самом деле, не надо.

bruno desthuilliers 18.01.2018 14:09

@brunodesthuilliers На самом деле есть случаи, когда это удобно :) Например, на ноутбуке IPython, и мы хотим, чтобы какая-то внутренняя функция открывала некоторые переменные глобально, чтобы мы могли поиграть с ней

ch271828n 27.02.2020 10:02

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

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

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

var_name = "my_useful_string"

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

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).

Лучше, потому что это позволяет избежать возможных конфликтов пространства имен

bgw 31.10.2010 04:45

Что, если модуль, который вы импортируете, в данном случае a.py, содержит main()? Это имеет значение?

sedeh 22.08.2014 22:11

@sedeh: нет. Если a.py также запускается как сценарий, используйте в нем защиту if __name__= = "__main__", чтобы избежать запуска неожиданного кода при импорте.

jfs 23.08.2014 16:31

В реальном мире с этим решением нужно быть немного осторожнее. Если программист выбирает вашу «глобальную» переменную с помощью «from a import var» (попробуйте этот вариант в c.py), он получит копию переменной во время импорта.

Paul Whipp 29.07.2015 02:36

@PaulWhipp: неправильный (подсказка: используйте id() для проверки личности)

jfs 29.07.2015 02:38

Шикарное решение! Правильно проголосовали большинство! Лучше, потому что это позволяет избежать возможных конфликтов пространства имен!

Eduardo Lucio 14.12.2015 22:57

@pevogam: нет, import не имеет объектов копировать. Бьюсь об заклад, вы использовали from a import var (var теперь находится в другом пространстве имен) вместо import a, как в моем ответе. (var остается в пространстве имен модуля a). Объект одинаковый, копии в обоих случаях нет. Связанный: Почему в Python функция может изменять одни аргументы, воспринимаемые вызывающей стороной, но не другие?

jfs 24.12.2015 01:49

Уловка: - Я сначала использовал from a import var, а var не было. - заменено на 'import a' и var на a.var, как написано выше, и это сработало!

herve-guerin 18.03.2017 17:37

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

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

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

import my_globals
print my_globals.foo

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

__builtins__ - это особенность CPython, вам действительно не стоит его использовать - лучше используйте __builtin__ (или builtins в Python3), как показывает принятый ответ

Tobias Kienzler 25.03.2015 16:58

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

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

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

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

Я использую это для пары встроенных примитивных функций, которых, как мне казалось, действительно не хватало. Одним из примеров является функция поиска, которая имеет ту же семантику использования, что и 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.

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

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

Когда есть только один такой модуль, я называю его «g». В нем я назначаю значения по умолчанию для каждой переменной, которую собираюсь рассматривать как глобальную. В каждом модуле, который использует любой из них, я не использую «from 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) полезно для обнаружения каких-либо неожиданных имен, которые могли возникнуть в результате такой случайности.

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

Curtis Yallop 20.10.2014 20:41

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

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

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

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

Например:

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

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

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

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

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Я называю этот модуль «стеной», поскольку он используется для отражения переменных. Он будет действовать как пространство для временного определения глобальных переменных и общеклассовых атрибутов пустого класса «router».

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

Другие вопросы по теме