W moich niekończących się poszukiwaniach zbyt skomplikowanych prostych rzeczy, badam najbardziej „Pythonowy” sposób dostarczania globalnych zmiennych konfiguracyjnych w typowym pliku „ config.py ”, który można znaleźć w pakietach Python egg.
Tradycyjny sposób (aaa, dobry stary #define !) Wygląda następująco:
MYSQL_PORT = 3306
MYSQL_DATABASE = 'mydb'
MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
Dlatego zmienne globalne są importowane w jeden z następujących sposobów:
from config import *
dbname = MYSQL_DATABASE
for table in MYSQL_DATABASE_TABLES:
print table
lub:
import config
dbname = config.MYSQL_DATABASE
assert(isinstance(config.MYSQL_PORT, int))
Ma to sens, ale czasami może być trochę nieuporządkowane, zwłaszcza gdy próbujesz zapamiętać nazwy pewnych zmiennych. Poza tym dostarczenie obiektu „konfiguracyjnego” ze zmiennymi jako atrybutami może być bardziej elastyczne. Tak więc, kierując się plikiem bpython config.py, wymyśliłem:
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)])
i „config.py”, który importuje klasę i czyta w następujący sposób:
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'
i jest używany w ten sposób:
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))
Co wydaje się bardziej czytelnym, wyrazistym i elastycznym sposobem przechowywania i pobierania zmiennych globalnych wewnątrz pakietu.
Najmodniejszy pomysł na świecie? Jaka jest najlepsza praktyka radzenia sobie w takich sytuacjach? Jaki jest twój sposób przechowywania i pobierania globalnych nazw i zmiennych wewnątrz pakietu?