Как создать модульные переменные в 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')
...

и вывод был такой: UnboundLocalError: local variable '__DBNAME__' referenced before assignment

какие идеи? Я пытаюсь настроить синглтон с помощью модуля, согласно рекомендации этого парня.

5 165

5 ответов:

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

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

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

представьте себе файл под названием foo.py, содержащий эту единственную строку:

X = 1

теперь представьте, что вы импортировать его.

import foo
print(foo.X)  # prints 1

однако предположим, что вы хотите использовать одну из переменных области модуля в качестве глобальной внутри функции, как в вашем примере. По умолчанию Python предполагает, что переменные функции являются локальными. Вы просто добавляете 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, вы не можете просто сказать if not variablename; в этом случае, вы должны четко проверить None С помощью is оператора. Я изменил пример, чтобы добавить явное

явный доступ к переменным уровня модуля путем доступа к ним explicity на модуле


короче: описанная здесь техника такая же, как в steveha это!--15-->,за исключением, что никакой искусственный вспомогательный объект не создается для явных переменных области. вместо этого сам объект модуля получает указатель переменной и поэтому обеспечивает явное определение области при доступе отовсюду. (например, назначения в области локальных функций).

что это self на модуль вместо текущего экземпляра !

# db.py
import sys

# this is a pointer to the module object instance itself.
this = sys.modules[__name__]

# we can explicitly make assignments on it 
this.db_name = None

def initialize_db(name):
    if (this.db_name is None):
        # also in local function scope. no scope specifier like global is needed
        this.db_name = name
        # also the name remains free for local use
        db_name = "Locally scoped db_name variable. Doesn't do anything here."
    else:
        msg = "Database is already initialized to {0}."
        raise RuntimeError(msg.format(this.db_name))

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

# client_a.py
import db

db.initialize_db('mongo')
# client_b.py
from db import db_name

if (db_name == 'mongo'):
    db_name = None

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

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

Если вы назначаете переменную без глобального ключевого слова, то Python создает новый локальный var - значение переменной модуля теперь будет скрыто внутри функции. Используйте ключевое слово global для назначения модуля var внутри функции.

Pylint 1.3.1 в Python 2.7 принудительно не используется global, если вы не назначаете var.

module_var = '/dev/hello'

def readonly_access():
    connect(module_var)

def readwrite_access():
    global module_var
    module_var = '/dev/hello2'
    connect(module_var)

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

global __DBNAME__

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

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

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

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