Как создать переменные всего модуля в Python? [дубликат]

Этот вопрос уже имеет ответ здесь:

Существует ли способ настроить глобальную переменную в модуле? Когда я пытался сделать это самый очевидный путь, как это появляется ниже, интерпретатор 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', на который ссылаются перед присвоением...

Какие-либо идеи? Я пытаюсь настроить одиночный элемент при помощи модуля согласно рекомендации этого товарища.

198
задан themefield 2 September 2019 в 18:40
поделиться

3 ответа

Вот что происходит.

Во-первых, единственные глобальные переменные, которые действительно есть у 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 в вышеприведенном примере.

.
225
ответ дан 23 November 2019 в 05:12
поделиться

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

Вы можете получить доступ к пространству имён модулей, вам просто не стоит пытаться переназначить. Если ваша функция что-то назначает, она автоматически становится переменной функции - и питон не будет искать в пространстве имён модуля.

Вы можете сделать:

__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__, я просто модифицирую его содержимое.

.
-7
ответ дан 23 November 2019 в 05:12
поделиться

Для этого необходимо объявить переменную глобальной. Однако, глобальная переменная также доступна из вне модуля с помощью module_name.var_name. Добавьте это в качестве первой строки модуля:

global __DBNAME__
6
ответ дан 23 November 2019 в 05:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: