В своем бесконечном поиске чрезмерно усложняющих простых вещей я исследую самый «питонический» способ предоставления глобальных переменных конфигурации внутри типичного файла config.py , который можно найти в пакетах яиц Python.
Традиционный способ (ах, старый добрый #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:%s@%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))
Это кажется более читаемым, выразительным и гибким способом хранения и выборки глобальных переменных внутри пакета.
Самая хромая идея? Как лучше всего справляться с такими ситуациями? Как вы храните и извлекаете глобальные имена и переменные внутри вашего пакета?
источник
python-box
, см. Этот ответОтветы:
Я сделал это однажды. В конечном итоге я счел упрощенный файл basicconfig.py подходящим для моих нужд. Вы можете передать пространство имен с другими объектами для ссылки, если вам нужно. Вы также можете передать дополнительные значения по умолчанию из своего кода. Он также сопоставляет синтаксис атрибутов и стиля сопоставления с одним и тем же объектом конфигурации.
источник
basicconfig.py
файл, похоже, переместился на github.com/kdart/pycopia/blob/master/core/pycopia/…ConfigHolder
с помощью набора конфигураций, который я хотел бы установить и передать между модулями?confit
который поддерживает объединение нескольких источников. Это часть нового модуля devtest.config .Как насчет использования таких встроенных типов:
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, для удобного анализа и доступа к файлу конфигурации
источник
Мне нравится это решение для небольших приложений :
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 ). Для небольших приложений это решение проще.
источник
Как насчет использования классов?
# config.py class MYSQL: PORT = 3306 DATABASE = 'mydb' DATABASE_TABLES = ['tb_users', 'tb_groups'] # main.py from config import MYSQL print(MYSQL.PORT) # 3306
источник
Подобно ответу blubb. Я предлагаю создавать их с помощью лямбда-функций, чтобы уменьшить количество кода. Как это:
User = lambda passwd, hair, name: {'password':passwd, 'hair':hair, 'name':name} #Col Username Password Hair Color Real Name config = {'st3v3' : User('password', 'blonde', 'Steve Booker'), 'blubb' : User('12345678', 'black', 'Bubb Ohaal'), 'suprM' : User('kryptonite', 'black', 'Clark Kent'), #... } #... config['st3v3']['password'] #> password config['blubb']['hair'] #> black
Однако это действительно пахнет, как будто вы, возможно, захотите создать класс.
Или, как заметил MarkM, вы можете использовать
namedtuple
from collections import namedtuple #... User = namedtuple('User', ['password', 'hair', 'name']} #Col Username Password Hair Color Real Name config = {'st3v3' : User('password', 'blonde', 'Steve Booker'), 'blubb' : User('12345678', 'black', 'Bubb Ohaal'), 'suprM' : User('kryptonite', 'black', 'Clark Kent'), #... } #... config['st3v3'].password #> passwd config['blubb'].hair #> black
источник
pass
- неудачное имя переменной, так как это также ключевое слово.mkDict
лямбда. Если мы вызовем наш классUser
, ваши ключи словаря "config" будут инициализированы примерно так{'st3v3': User('password','blonde','Steve Booker')}
. Когда ваш «пользователь» находится вuser
переменной, вы можете получить доступ к его свойствам какuser.hair
и т. Д.User = namedtuple('User', 'passwd hair name'); config = {'st3v3': User('password', 'blonde', 'Steve Booker')}
Небольшая вариация идеи Хаски, которую я использую. Создайте файл с именем '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, вы можете иметь несколько таких файлов и переменных, которые позволяют получать доступ и устанавливать значения в файлах кода, функциях и т. Д., Но без опасности столкновения пространств имен.
В нем отсутствуют некоторые из умных методов проверки ошибок, свойственных другим подходам, но он прост и понятен.
источник
globals
, поскольку это встроенная функция, которая возвращает dict с каждым символом в текущей глобальной области видимости. Кроме того, PEP8 рекомендует CamelCase (со всеми заглавными буквами в акронимах) для классов (т.е.DBInfo
) и прописными буквами с подчеркиванием для так называемых констант (т.е.DEBUG
).globals
, автор должен изменить имяДавайте будем честными, нам, вероятно, следует рассмотреть возможность использования поддерживаемой библиотеки Python Software Foundation :
https://docs.python.org/3/library/configparser.html
Пример конфигурации: (формат ini, но доступен JSON)
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
Пример кода:
>>> import configparser >>> config = configparser.ConfigParser() >>> config.read('example.ini') >>> config['DEFAULT']['Compression'] 'yes' >>> config['DEFAULT'].getboolean('MyCompression', fallback=True) # get_or_else
Сделаем его глобально доступным:
import configpaser class App: __conf = None @staticmethod def config(): if App.__conf is None: # Read only once, lazy. App.__conf = configparser.ConfigParser() App.__conf.read('example.ini') return App.__conf if __name__ == '__main__': App.config()['DEFAULT']['MYSQL_PORT'] # or, better: App.config().get(section='DEFAULT', option='MYSQL_PORT', fallback=3306) ....
Минусы:
источник
Пожалуйста, ознакомьтесь с системой конфигурации IPython, реализованной через трейтлеты для принудительного применения типов, которые вы выполняете вручную.
Вырезано и вставлено здесь, чтобы соответствовать руководящим принципам SO, а не просто отбрасывать ссылки, поскольку их содержимое со временем меняется.
документация traitlets
Чтобы добиться этого, они в основном определяют 3 класса объектов и их отношения друг к другу:
1) Конфигурация - в основном ChainMap / basic dict с некоторыми улучшениями для слияния.
2) Настраиваемый - базовый класс для подкласса всего, что вы хотите настроить.
3) Приложение - объект, экземпляр которого создается для выполнения определенной функции приложения, или ваше основное приложение для одноцелевого программного обеспечения.
По их словам:
источник