Улучшение управления конфигурационным файлом
Я работаю над личным проектом более 6 месяцев, этот проект состоит из трех отдельных частей: моделирования (упаковка программного обеспечения), материалов, связанных с базой данных (сохранение смоделированных данных в базе данных) и моделирования (много машинного обучения связанные вещи)
Чтобы управлять своей конфигурацией (пути и несколько констант), я начал с помещения всех переменных в файл config.py в корне пакета (см. Ниже). Это сработало на удивление хорошо. Однако есть предостережение, я обычно работаю с симуляцией и базой данных в тандеме или с базой данных и моделированием в тандеме, дело в том, что в этой настройке у меня есть весь путь к проекту в зависимости от одного экспериментального имени, присутствующего в файле конфигурации.
Эта ситуация сработала на удивление хорошо, когда я работал только с базой данных и моделированием, но сейчас проект растет, и это уже не так хорошо.
Теперь мне нужно будет иметь возможность изменить «имя эксперимента» при запуске теста, однако мне нравится тот факт, что я могу «импортировать» весь путь и константу, просто импортировав конфигурацию как c, (тогда я могу сделать c.path .. и знать, откуда взялась переменная).
Я ищу новый способ обработки путей и переменной конфигурации в моем проекте. Я думаю о том, чтобы поместить весь файл конфигурации в класс и сделать что-то вроде c = Config (имя_эксперимента), у меня есть два вопроса, связанных с этим:
- Это хорошая идея / хорошая практика? Есть ли лучший вариант?
- Где мне создать объект внутри основных файлов?
Структура проекта
├── package
│ ├── __init__.py
│ ├── config.py
│ ├── simulation
│ │ ├── __init__.py
│ │ ├── core.py
│ │ └── utilities.py
│ ├── database
│ │ ├── __init__.py
│ │ ├── initialization.py
│ │ ├── insert.py
│ │ └── retrieve.py
│ └── modelization
│ ├── __init__.py
│ ├── preprocessing.py
│ └── training.py
├── tests
│ ├── test_simulation
│ │ ├── test_run_simulation.py
│ │ └── test_multiprocessing.py
│ └── test_database
│ ├── test_initialization.py
│ ├── test_insert.py
│ └── test_retrieve.py
├── databases
│ └── experiment_0
│ ├── train.db
│ └── test.db
├── FOLDER
└── experiments
└── experiment_0
├── output.json
├── input.json
└── source
└── lot_of_files_for_simulation
config.py
import socket
from pathlib import Path, PurePath
experiment_name="experiment_0"
enable_simulation = True
development_hostname="DESKTOP-VXXXX"
path_project = Path(__file__).parent.parent
path_experiments_folder = path_project.joinpath('experiments')
path_experiment_folder = path_experiments_folder.joinpath(experiment_name)
path_experiment_input_config = path_experiment_folder.joinpath('input.json')
path_experiment_output_config = path_experiment_folder.joinpath('output.json')
path_databases_folder = path_project.joinpath('databases')
path_experiment_database_folder = path_databases_folder.joinpath(experiment_name)
if socket.gethostname() != development_hostname and enable_simulation:
run_simulation = True
path_simulation_root_directory = PurePath('/home', 'thomas', 'FOLDER')
else:
run_simulation = False
path_simulation_root_directory = path_project.joinpath('FOLDER')
state = f"hostname {socket.gethostname()}, simulation {['OFF', 'ON'][run_simulation]}"
моделирование / core.py
from .. import config as c
from utilities import sub_function
def function_that_i_call_in_my_scripts(path_experiment_input_config=None):
path = path_experiment_input_config or c.path_experiment_input_config
sub_function(path)
class ClassThatICallInMyScripts:
def __init__(self, path_experiment_input_config=None):
self.path_experiment_input_config = path_experiment_input_config or c.path_experiment_input_config
def foo(self):
return # do something
моделирование / utilities.py
def sub_function(path):
print(f'do something in {path}')
идея нового config.py
import socket
from pathlib import Path, PurePath
class Config:
def __init__(self, experiment_name, enable_simulation=True, development_hostname="DESKTOP-VXXXX"):
self.experiment_name = experiment_name
self.enable_simulation = enable_simulation
self.development_hostname = development_hostname
self.path_project = Path(__file__).parent.parent
self.path_experiments_folder = self.path_project.joinpath('experiments')
self.path_experiment_folder = self.path_experiments_folder.joinpath(experiment_name)
self.path_experiment_input_config = self.path_experiment_folder.joinpath('input.json')
self.path_experiment_output_config = self.path_experiment_folder.joinpath('output.json')
self.path_databases_folder = self.path_project.joinpath('databases')
self.path_experiment_database_folder = self.path_databases_folder.joinpath(experiment_name)
if socket.gethostname() != development_hostname and enable_simulation:
self.run_simulation = True
self.path_simulation_root_directory = PurePath('/home', 'thomas', 'FOLDER')
else:
self.run_simulation = False
self.path_simulation_root_directory = self.path_project.joinpath('FOLDER')
self.state = f"hostname {socket.gethostname()}, simulation {['OFF', 'ON'][self.run_simulation]}"
1 ответ
Я думаю, у вас должен быть каталог для конфигураций, и в этом каталоге должны быть разные подкаталоги со средами, которые вы считаете удобными для реализации, самые основные из них: производство, качество, разработка, локальный
Имейте в виду, что также рекомендуется, чтобы приложение контролировало переменные конфигурации и проверяло, существуют ли они во всех средах. значения могут измениться.
это:
├── package
│ ├── __init__.py
│ ├── config.py
│ ├── simulation
│ │ ├── core.py
│ │ └── utilities.py
должно быть:
├── package
│ ├── __init__.py
│ ├── config
│ │ ├──production
│ │ │ └──config.py
│ │ ├──development
│ │ │ └──config.py
│ │ ├──quality
│ │ │ └──config.py
│ │ ├──local
│ │ │ └──config.py
│ │ └──configmanager.py
│ ├── simulation
│ │ ├── core.py
│ │ └── utilities.py
даже если вы используете разные конфигурации базы данных, вы можете сделать что-то вроде:
├── package
│ ├── __init__.py
│ ├── config
│ │ ├──production
│ │ │ └──config.py
│ │ ├──development
│ │ │ └──config.py
│ │ ├──quality
│ │ │ └──config.py
│ │ ├──local
│ │ │ └──config.py
│ │ ├──dbconfig
│ │ │ ├──dbconfig1.py
│ │ │ ├──dbconfig2.py
│ │ │ ├──dbconfig3.py
│ │ │ ├──dbconfig4.py
│ │ │ ├──dbconfig5.py
│ │ └──configmanager.py
│ ├── simulation
│ │ ├── core.py
│ │ └── utilities.py
На уровне структуры и развития это покажет, что существует организация данных конфигурации.
Я также считаю, что вы можете связать эксперимент с базой данных, которую вы должны использовать … это можно сделать на уровне пользователя или вы можете запрограммировать его статическим.
Я не совсем уверен, что понимаю первую часть вашего ответа. Можно ли показать крошечный пример, чтобы я мог лучше его понять? Хорошее предложение для приложения, которое контролирует конфигурацию, я сохраню это в моем списке задач 🙂 сейчас я только на этапе проверки концепции
— th0mash
@ th0mash, я обновил диаграммы
— Франсиско Нуньес
Думаю, я понимаю ваш ответ, однако для меня самой большой проблемой в настоящее время является то, что я хотел бы иметь возможность «вводить»
experiment_name
в моей конфигурации, чтобы иметь настраиваемые пути в зависимости от того, над каким экспериментом я работаю: / Для ассоциации с базой данных вы видите путь к папке базы данных в файле конфигурации, после этого я просто добавляю, если я в настоящее время работаю над «поездом» или «тестовая» дб. Что будет делать configmanager? объединить все файлы конфигурации?— th0mash
«configmanager» — это программа, позволяющая пользователю управлять всеми возможными конфигурациями графическим способом, и это поможет избежать пустых переменных.
— Франсиско Нуньес
будет ближе всего к вводу …
— Франсиско Нуньес