У своєму нескінченному пошуку надто ускладнюючих простих речей, я досліджую найбільш «Pythonic» спосіб надання глобальних змінних конфігурації всередині типового « 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))
Що здається більш читабельним, виразним та гнучким способом зберігання та отримання глобальних змінних всередині пакету.
Найгарніша ідея коли-небудь? Яка найкраща практика для подолання цих ситуацій? Що ваш спосіб зберігання і вибірки глобальні імена і змінні всередині пакету?