У мене є три модулі:
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 / Ваше бокове питання: так, наявність якогось "конфігураційного" об'єкта є досить поширеною схемою. Ви просто можете переконатися, що ви можете створити його "з нуля" (я маю на увазі, не обов'язково з конфігураційного файлу), щоб полегшити тестування.