Большинство Pythonic способ предоставить глобальные переменные конфигурации в config.py?
В моем бесконечном поиске в чрезмерном усложнении простых вещей я изучаю наиболее "путинский" способ предоставления глобальных переменных конфигурации внутри типичного " config.py", найденного в пакетах яиц Python.
Традиционный способ (aah, good ol '#define!) выглядит следующим образом:
MYSQL_PORT = 3306
MYSQL_DATABASE = 'mydb'
MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
Поэтому глобальные переменные импортируются одним из следующих способов:
from config import *
dbname = MYSQL_DATABASE
for table in MYSQL_DATABASE_TABLES:
print table
или
import config
dbname = config.MYSQL_DATABASE
assert(isinstance(config.MYSQL_PORT, int))
Это имеет смысл, но иногда может быть немного грязным, особенно когда вы пытаетесь запомнить имена определенных переменных. Кроме того, предоставление 'конфигурации объекта в качестве атрибутов может быть более гибким. Итак, взяв лидерство из файла конфигурации bpython config.py, я придумал:
class Struct(object):
def __init__(self, *args):
self.__header__ = str(args[0]) if args else None
def __repr__(self):
if self.__header__ is None:
return super(Struct, self).__repr__()
return self.__header__
def next(self):
""" Fake iteration functionality.
"""
raise StopIteration
def __iter__(self):
""" Fake iteration functionality.
We skip magic attribues and Structs, and return the rest.
"""
ks = self.__dict__.keys()
for k in ks:
if not k.startswith('__') and not isinstance(k, Struct):
yield getattr(self, k)
def __len__(self):
""" Don't count magic attributes or Structs.
"""
ks = self.__dict__.keys()
return len([k for k in ks if not k.startswith('__')\
and not isinstance(k, Struct)])
и "config.py", который импортирует класс и читается следующим образом:
from _config import Struct as Section
mysql = Section("MySQL specific configuration")
mysql.user = 'root'
mysql.pass = 'secret'
mysql.host = 'localhost'
mysql.port = 3306
mysql.database = 'mydb'
mysql.tables = Section("Tables for 'mydb'")
mysql.tables.users = 'tb_users'
mysql.tables.groups = 'tb_groups'
и используется таким образом:
from sqlalchemy import MetaData, Table
import config as CONFIG
assert(isinstance(CONFIG.mysql.port, int))
mdata = MetaData(
"mysql://%s:%[email protected]%s:%d/%s" % (
CONFIG.mysql.user,
CONFIG.mysql.pass,
CONFIG.mysql.host,
CONFIG.mysql.port,
CONFIG.mysql.database,
)
)
tables = []
for name in CONFIG.mysql.tables:
tables.append(Table(name, mdata, autoload=True))
Что представляется более читаемым, выразительным и гибким способом хранения и выборки глобальных переменных внутри пакета.
Самая известная идея? Какова наилучшая практика для решения этих ситуаций? Каков ваш способ хранения и выбор глобальных имен и переменных внутри вашего пакета?
Ответы
Ответ 1
Я сделал это один раз. В конечном итоге я нашел свой упрощенный basicconfig.py, соответствующий моим потребностям. Вы можете пройти в пространстве имен с другими объектами для ссылки, если вам нужно. Вы также можете передать дополнительные значения по умолчанию из своего кода. Он также отображает синтаксис стиля атрибута и отображения в один и тот же объект конфигурации.
Ответ 2
Как использовать только встроенные типы:
config = {
"mysql": {
"user": "root",
"pass": "secret",
"tables": {
"users": "tb_users"
}
# etc
}
}
Вы получите доступ к следующим значениям:
config["mysql"]["tables"]["users"]
Если вы готовы пожертвовать потенциалом для вычисления выражений внутри вашего дерева конфигураций, вы можете использовать YAML и в итоге получить больше читаемый файл конфигурации следующим образом:
mysql:
- user: root
- pass: secret
- tables:
- users: tb_users
и используйте библиотеку, например PyYAML, чтобы упростить анализ и доступ к файлу конфигурации
Ответ 3
Аналогично запросу blubb. Мне нравятся встроенные типы. Я предлагаю создать их с помощью лямбда-функций, если сможете. Вот так:
mkDict = lambda passwd, hair, name: {'passwd':passwd, 'hair':hair, 'name':name}
#Col Names: Password Hair Color Real Name
config = {'st3v3' : mkDict('password', 'blonde', 'Steve Booker'),
'blubb' : mkDict('12345678', 'black', 'Bubb Ohaal'),
'suprM' : mkDict('kryptonite', 'black', 'Clark Kent'),
#...
}
Да, теперь вам не нужно так много копировать. С комментариями также легче сравнивать и читать данные позже.
Ответ 4
Как насчет использования классов?
# config.py
class MYSQL:
PORT = 3306
DATABASE = 'mydb'
DATABASE_TABLES = ['tb_users', 'tb_groups']
# main.py
from config import MYSQL
print(MYSQL.PORT) # 3306
Ответ 5
Мне нравится это решение для небольших приложений:
class App:
__conf = {
"username": "",
"password": "",
"MYSQL_PORT": 3306,
"MYSQL_DATABASE": 'mydb',
"MYSQL_DATABASE_TABLES": ['tb_users', 'tb_groups']
}
__setters = ["username", "password"]
@staticmethod
def config(name):
return App.__conf[name]
@staticmethod
def set(name, value):
if name in App.__setters:
App.__conf[name] = value
else:
raise NameError("Name not accepted in set() method")
И тогда используется следующее:
if __name__ == "__main__":
# from config import App
App.config("MYSQL_PORT") # return 3306
App.set("username", "hi") # set new username value
App.config("username") # return "hi"
App.set("MYSQL_PORT", "abc") # this raises NameError
.. вам это понравится, потому что:
- использует переменные класса (нет объекта для обхода/отсутствия одиночного элемента),
- использует инкапсулированные встроенные типы и выглядит как() вызов метода на
App
,
- имеет контроль над индивидуальной конфигурацией неизменяемости, изменяемые глобальные переменные являются наихудшим видом глобальных переменных.
- поддерживает обычный и хорошо названный доступ/читабельность в исходном коде
- является простым классом, но обеспечивает структурированный доступ, альтернативой является использование
@property
, но для этого требуется больше переменной кода обработки для каждого элемента и основано на объектах.
- требует минимальных изменений, чтобы добавить новые элементы конфигурации и установить его изменчивость.
- Edit -:
Для больших приложений сохранение значений в файле YAML (т.е. свойств) и чтение, которое в качестве неизменяемых данных является лучшим подходом (т.е. ответ blubb/ohaal).
Для небольших приложений это решение выше проще.
Ответ 6
Небольшая вариация идеи Хаски, которую я использую. Создайте файл под названием "globals" (или как вам нравится), а затем определите в нем несколько классов:
#globals.py
class dbinfo : # for database globals
username = 'abcd'
password = 'xyz'
class runtime :
debug = False
output = 'stdio'
Затем, если у вас есть два файла кода c1.py и c2.py, оба могут иметь верхнюю часть
import globals as gl
Теперь весь код может получить доступ и установить значения как таковые:
gl.runtime.debug = False
print(gl.dbinfo.username)
Люди забывают, что классы существуют, даже если ни один объект никогда не создается, который является членом этого класса. И переменные в классе, которым не предшествует "я". разделяются во всех экземплярах класса, даже если их нет. После того, как "отладка" будет изменена любым кодом, весь другой код увидит изменение.
Импортируя его в виде gl, вы можете иметь несколько таких файлов и переменных, которые позволяют вам получать и устанавливать значения между кодовыми файлами, функциями и т.д., но без угрозы столкновения пространства имен.
Этому недостает некоторой умной проверки ошибок других подходов, но просто и легко следовать.
Ответ 7
пожалуйста, проверьте конфигурационную систему IPython, реализованную через traitsts для принудительного ввода типа, который вы делаете вручную.
Вырезать и вставлять здесь, чтобы соответствовать рекомендациям SO не только для удаления ссылок, поскольку содержимое ссылок изменяется с течением времени.
трассировать документацию
Вот основные требования, которые мы хотели, чтобы наша система конфигурации имела:
Поддержка иерархической информации о конфигурации.
Полная интеграция с парсерами параметров командной строки. Часто вы хотите прочитать файл конфигурации, но затем переопределите некоторые значения с параметрами командной строки. Наша система конфигурации автоматизирует этот процесс и позволяет связать каждую опцию командной строки с определенным атрибутом в иерархии конфигурации, который будет отменен.
Конфигурационные файлы, которые сами являются действительными Python-кодом. Это многое выполняет. Во-первых, становится возможным поставить логику в ваши файлы конфигурации, которая устанавливает атрибуты на основе вашей операционной системы, настройки сети, версии Python и т.д. Во-вторых, Python имеет супер простой синтаксис для доступа к иерархическим структурам данных, а именно к регулярному доступу к атрибутам (Foo. Bar.Bam.name). В-третьих, использование Python упрощает пользователям импорт атрибутов конфигурации из одного конфигурационного файла в другой. В-четвертых, хотя Python динамически типизирован, он имеет типы, которые можно проверить во время выполнения. Таким образом, 1 в файле конфигурации является целым числом '1, а' 1 '- строкой.
Полностью автоматизированный метод получения информации о конфигурации для классов, которые нуждаются в ней во время выполнения. Написание кода, который выполняет иерархию конфигурации для извлечения определенного атрибута, является болезненным. Когда у вас есть сложная информация о конфигурации с сотнями атрибутов, это заставляет вас плакать.
Проверка и проверка типов, которые не требуют, чтобы вся иерархия конфигурации была указана статически перед запуском. Python - очень динамичный язык, и вы не всегда знаете все, что нужно настроить при запуске программы.
Чтобы добиться этого, они в основном определяют 3 класса объектов и их отношения друг к другу:
1) Конфигурация - в основном ChainMap/basic dict с некоторыми улучшениями для слияния.
2) Настраиваемый - базовый класс для подкласса всех вещей, которые вы хотите настроить.
3) Приложение - объект, который создается для выполнения определенной прикладной функции или вашего основного приложения для одноцелевого программного обеспечения.
По их словам:
Приложение: Приложение
Приложение - это процесс, выполняющий определенное задание. Наиболее очевидным приложением является программа командной строки ipython. Каждое приложение считывает один или несколько файлов конфигурации и один набор параметров командной строки, а затем создает главный объект конфигурации для приложения. Затем этот объект конфигурации передается настраиваемым объектам, созданным приложением. Эти настраиваемые объекты реализуют фактическую логику приложения и знают, как настроить себя с учетом объекта конфигурации.
Приложения всегда имеют атрибут журнала, который является настроенным Logger. Это позволяет централизованную конфигурацию протоколирования для каждого приложения. Конфигурируемый: настраиваемый
Конфигурируемый - это обычный класс Python, который служит базовым классом для всех основных классов в приложении. Настраиваемый базовый класс является легким и делает только одно.
Этот Configurable является подклассом HasTraits, который знает, как настроить себя. Значения уровня класса с метаданными config = True становятся значениями, которые могут быть сконфигурированы из командной строки и файлов конфигурации.
Разработчики создают настраиваемые подклассы, реализующие всю логику приложения. Каждый из этих подклассов имеет свою собственную конфигурационную информацию, которая управляет созданием экземпляров.