Этот вопрос уже имеет ответ здесь:
Существует ли способ настроить глобальную переменную в модуле? Когда я пытался сделать это самый очевидный путь, как это появляется ниже, интерпретатор Python сказал переменную __DBNAME__
не существовал.
...
__DBNAME__ = None
def initDB(name):
if not __DBNAME__:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
...
И после импорта модуля в другом файле
...
import mymodule
mymodule.initDB('mydb.sqlite')
...
И traceback был:
... UnboundLocalError: локальная переменная 'DBNAME', на который ссылаются перед присвоением...
Какие-либо идеи? Я пытаюсь настроить одиночный элемент при помощи модуля согласно рекомендации этого товарища.
Вот что происходит.
Во-первых, единственные глобальные переменные, которые действительно есть у Python - это переменные с модульной обработкой. Вы не можете сделать действительно глобальную переменную; всё, что вы можете сделать - это сделать переменную в определённой области видимости. (Если вы делаете переменную внутри интерпретатора Python, а затем импортируете другие модули, ваша переменная находится во внешней области видимости и, таким образом, глобальна внутри вашего сеанса работы с Python)
Все, что вам нужно сделать, чтобы сделать модуль-глобальную переменную, это просто присвоить ей имя.
Представьте себе файл с именем foo.py, содержащий эту единственную строку:
X = 1
Теперь представьте, что вы импортируете его.
import foo
print(foo.X) # prints 1
Однако, давайте предположим, что вы хотите использовать одну из ваших переменных модульного сканирования как глобальную внутри функции, как в вашем примере. По умолчанию Python предполагает, что переменные функции являются локальными. Вы просто добавляете в свою функцию объявление global
, прежде чем попытаться использовать global.
def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
Кстати, для этого примера простой тест if not __DBNAME__
является адекватным, так как любое значение строки, кроме пустой строки, будет оценивать true, а значит, любое действительное имя БД будет оценивать true. Но для переменных, которые могут содержать числовое значение, которое может быть 0, нельзя просто сказать , если не вариабельное имя
, в таком случае следует явно проверить None
, используя оператор is
. Я изменил пример, добавив явный тест None
. Явный тест для None
никогда не ошибается, поэтому я использую его по умолчанию.
Наконец, как другие отмечали на этой странице, два ведущих сигнала подчеркивания для Python о том, что вы хотите, чтобы переменная была "приватной" для модуля. Если вы когда-нибудь импортируете * из mymodule
, Python не будет импортировать имена с двумя ведущими символами подчеркивания в ваше пространство имен. Но если вы просто импортируете -модуль
и затем скажете dir(mymodule)
, вы увидите "private" переменные в списке, и если вы явно обратитесь к mymodule.__DBNAME__
Python не будет беспокоиться, он просто даст вам на него ссылаться. Двойные ведущие подчеркивания - это основная подсказка для пользователей вашего модуля, что вы не хотите, чтобы они перепривязывали это имя к какому-то собственному значению.
Считается лучшей практикой в Python не делать импорт *
, а минимизировать связь и максимизировать явность либо с помощью mymodule. Что-то
или явно делая импорт типа из импорта что-то
.
EDIT: Если по какой-то причине вам нужно сделать что-то подобное в очень старой версии Python, в которой нет ключевого слова global
, есть простое решение проблемы. Вместо того, чтобы напрямую задавать модульную глобальную переменную, используйте мутируемый тип на модульном глобальном уровне, и храните свои значения внутри него.
В ваших функциях имя глобальной переменной будет доступно только для чтения; вы не сможете перепривязать действительное имя глобальной переменной. (Если вы присваиваете этому имени переменной внутри вашей функции, это повлияет только на имя локальной переменной внутри функции). Но вы можете использовать это имя локальной переменной для доступа к реальному глобальному объекту и хранения данных внутри него.
Вы можете использовать list
, но ваш код будет некрасивым:
__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
A dict
лучше. Но самым удобным является экземпляр класса, а можно просто использовать тривиальный класс:
class Box:
pass
__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module
# later, in code:
if __m.dbname is None:
__m.dbname = name
(На самом деле переменная имени базы данных не нужна заглавная)
Мне нравится синтаксический сахар просто использования __m.dbname
, а не __m["DBNAME"]
; на мой взгляд, это наиболее удобное решение. Но решение dict
также отлично работает.
С помощью dict
вы можете использовать любое хэшируемое значение в качестве ключа, но когда вас устраивают имена, которые являются действительными идентификаторами, вы можете использовать тривиальный класс, как Box
в вышеприведенном примере.
Ты влюбляешься в тонкую причуду. Вы не можете переназначить переменные модульного уровня внутри питоновой функции. Думаю, это сделано для того, чтобы остановить случайное переназначение вещей внутри функции.
Вы можете получить доступ к пространству имён модулей, вам просто не стоит пытаться переназначить. Если ваша функция что-то назначает, она автоматически становится переменной функции - и питон не будет искать в пространстве имён модуля.
Вы можете сделать:
__DB_NAME__ = None
def func():
if __DB_NAME__:
connect(__DB_NAME__)
else:
connect(Default_value)
но вы не можете переприсвоить __DB_NAME__
внутри функции.
Один обходной путь:
__DB_NAME__ = [None]
def func():
if __DB_NAME__[0]:
connect(__DB_NAME__[0])
else:
__DB_NAME__[0] = Default_value
Обратите внимание, я не переназначаю __DB_NAME__
, я просто модифицирую его содержимое.
Для этого необходимо объявить переменную глобальной. Однако, глобальная переменная также доступна из вне модуля с помощью module_name.var_name
. Добавьте это в качестве первой строки модуля:
global __DBNAME__