Большинство 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 становятся значениями, которые могут быть сконфигурированы из командной строки и файлов конфигурации.

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