/ / Імпорт змінної безпосередньо дає інше значення, ніж імпорт її модуля в Python - python, python-import

Імпортування змінної безпосередньо дає інше значення, ніж імпорт його модуля в Python - python, python-import

У мене є три модулі:

  • constants, який містить завантажену конфігурацію та інші речі в класі
  • main, яка ініціалізується constants коли вона запущена
  • user, який імпортує constants і отримує доступ до його конфігурації.

constants Модуль (спрощений) виглядає приблизно так:

class Constants:
def __init__(self, filename):
# Read values from INI file
config = self.read_inifile(filename)
self.somevalue = config["ex"]["ex"]

def read_inifile(self, filename):
# reads inifile

constants: Constants = None

def populate_constants(filename):
global constants
constants = Constants(filename)

Простий об’єкт, який повинен містити конфігурацію, нічого не представляє собою новаторських.

Функція populate_constants() викликається з main при запуску програми.

Зараз дивовижі бувають - коли я імпортую constants модуль від user як це, то constants є None:

from toplevelpkg.constants import constants
print(constants)

None

Однак якщо я імпортую його так, constants ініціалізується так, як можна було б очікувати:

from toplevelpkg import constants
print(constants.constants)

<trumpet.constants.Constants object at 0x035AA130>

Чому так?

EDIT: у моєму коді функція, яка намагається прочитати constants запускається асинхронно через await loop.run_in_executor(None, method_needing_import). Не впевнені, чи це може спричинити проблеми?

(і як побічне запитання, чи є хорошою практикою наявність об'єкта, що тримає конфігурацію, який аналізує файл конфігурації та надає його як його змінні члена?)

Відповіді:

2 для відповіді № 1

Існує різниця між

from mymodule import obj
(...)
do_something_with(obj)

і

import mymodule
(...)
do_something_with(mymodule.obj)

У першому випадку він діє як:

 import mymodule
obj = mymodule.obj
del mymodule

що означає, що в цей момент у поточному модулі, obj це "глобальний" (що насправді в python означає "рівень модуля", а не "загальноприкладний") ім'я, пов'язане з будь-яким mymodule.obj був коли він був імпортований (у вашому випадку: None). З тих пір, mymodule.obj і модуль-локальний obj імена живуть у різних просторах імен (перший у mymodule простір імен, другий - у поточному просторі імен модуля) та рендинг mymodule.obj з будь-якого місця не вийде нічого змінити на те, що в поточному модулі obj прив’язаний до. Насправді це точно так, як якщо б ви робили це:

a = 2
b = a
a = 4

Після третього твердження, b очевидно, все ще зобов'язаний 2 - ребрінг a до 4 не впливає b.

У другому випадку (import mymodule) те, що пов'язане в просторі імен модуля імпорту, є цілим mymodule об'єкт, так що якщо mymodule.obj отримує відскок (зсередини mymodule або деінде) зміни будуть видимі в модулі імпорту. У цьому випадку це еквівалентно

a = {"x": 2}
b = a
a["x"] = 4

У такому випадку зміна буде видно з b["x"] а також з тих пір a і b все ще пов'язані з тим самим об’єктом.

wrt / Ваше бокове питання: так, наявність якогось "конфігураційного" об'єкта є досить поширеною схемою. Ви просто можете переконатися, що ви можете створити його "з нуля" (я маю на увазі, не обов'язково з конфігураційного файлу), щоб полегшити тестування.