Вопрос-Ответ

Visibility of global variables in imported modules

Видимость глобальных переменных в импортированных модулях

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

Предположим, у меня есть модуль, в котором я определил некоторые служебные функции / классы, которые ссылаются на сущности, определенные в пространстве имен, в которое будет импортирован этот вспомогательный модуль (пусть "a" будет такой сущностью):

module1:

def f():
print a

И затем у меня есть основная программа, где определено "a", в которую я хочу импортировать эти утилиты:

import module1
a=3
module1.f()

Выполнение программы вызовет следующую ошибку:

Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.f()
File "Z:\Python\module1.py", line 3, in f
print a
NameError: global name 'a' is not defined

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

Я пытаюсь создать программу на Python, которая подключается к серверу базы данных MySQL и отображает / изменяет данные с помощью графического интерфейса. Для чистоты я определил набор вспомогательных / служебных функций, связанных с MySQL, в отдельном файле. Однако все они имеют общую переменную, которую я изначально определил внутри модуля utilities и которая является объектом cursor из модуля MySQLdb. Позже я понял, что объект cursor (который используется для связи с сервером БД) должен быть определен в основном модуле, чтобы и основной модуль, и все, что в него импортируется, могли обращаться к этому объекту.

Конечный результат будет примерно таким:

utilities_module.py:

def utility_1(args):
code which references a variable named "cur"
def utility_n(args):
etcetera

И мой основной модуль:

program.py:

import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *

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

Особое предложение заключалось в том, чтобы иметь инструкцию "from program import cur" в файле утилит, такую как эта:

utilities_module.py:

from program import cur
#rest of function definitions

program.py:

import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *

Но это циклический импорт или что-то в этом роде, и, в конечном итоге, это тоже приводит к сбою. Итак, мой вопрос:

Как, черт возьми, я могу сделать объект "cur", определенный в основном модуле, видимым для тех вспомогательных функций, которые в него импортируются?

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

Переведено автоматически
Ответ 1

Глобальные переменные в Python являются глобальными для модуля, а не для всех модулей. (Многих людей это смущает, потому что, скажем, в C глобальная переменная одинакова во всех файлах реализации, если вы явно не укажете это static.)

Существуют разные способы решить эту проблему, в зависимости от вашего реального варианта использования.


Прежде чем идти по этому пути, спросите себя, действительно ли это должно быть глобальным. Может быть, вам действительно нужен класс с f в качестве метода экземпляра, а не просто свободная функция? Тогда вы могли бы сделать что-то вроде этого:

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

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

import module1
module1.a=3
module1.f()

С другой стороны, если a используется множеством модулей, поместите его куда-нибудь еще и попросите всех импортировать его:

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

... и, в module1.py:

import shared_stuff
def f():
print shared_stuff.a

Не используйте from импорт, если переменная не должна быть константой. from shared_stuff import a создаст новую a переменную, инициализированную тем, на что shared_stuff.a ссылается во время импорта, и на эту новую a переменную не будут влиять присвоения shared_stuff.a.


Или, в том редком случае, когда вам действительно нужно, чтобы он был действительно глобальным везде, как встроенный, добавьте его во встроенный модуль. Точные детали различаются в Python 2.x и 3.x. В 3.x это работает следующим образом:

import builtins
import module1
builtins.a = 3
module1.f()
Ответ 2

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

main.py:

import os
os.environ['MYVAL'] = str(myintvariable)

mymodule.py:

import os

myval = None
if 'MYVAL' in os.environ:
myval = os.environ['MYVAL']

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

Ответ 3

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

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

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Затем у меня был основной модуль, который импортирует общий материал с:

import sharedstuff as shared

и некоторые другие модули, которые фактически заполняли эти массивы. Они вызываются основным модулем. При выходе из этих других модулей я могу ясно видеть, что массивы заполнены. Но при чтении их обратно в основном модуле они были пустыми. Для меня это было довольно странно (ну, я новичок в Python). Однако, когда я меняю способ импорта sharedstuff.py в основном модуле на:

from globals import *

это сработало (массивы были заполнены).

Просто говорю

Ответ 4

Функция использует глобальные переменные модуля, в котором она определена. Например, вместо установки a = 3 вы должны устанавливать module1.a = 3. Итак, если вы хотите, чтобы cur они были доступны как глобальные в utilities_module, установите utilities_module.cur.

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

python