En mi búsqueda interminable de complicar demasiado cosas simples, estoy investigando la forma más 'Pythonic' de proporcionar variables de configuración global dentro del típico ' config.py ' que se encuentra en los paquetes de huevos de Python.
La forma tradicional (¡ah, bueno #define !) Es la siguiente:
MYSQL_PORT = 3306
MYSQL_DATABASE = 'mydb'
MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
Por lo tanto, las variables globales se importan de una de las siguientes formas:
from config import *
dbname = MYSQL_DATABASE
for table in MYSQL_DATABASE_TABLES:
print table
o:
import config
dbname = config.MYSQL_DATABASE
assert(isinstance(config.MYSQL_PORT, int))
Tiene sentido, pero a veces puede ser un poco complicado, especialmente cuando intentas recordar los nombres de ciertas variables. Además, proporcionar un objeto de 'configuración' , con variables como atributos , podría ser más flexible. Entonces, tomando una iniciativa del archivo bpython config.py, se me ocurrió:
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)])
y un 'config.py' que importa la clase y dice lo siguiente:
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'
y se usa de esta manera:
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))
Lo que parece una forma más legible, expresiva y flexible de almacenar y obtener variables globales dentro de un paquete.
¿La idea más tonta de todas? ¿Cuál es la mejor práctica para hacer frente a estas situaciones? ¿Cuál es su forma de almacenar y obtener nombres y variables globales dentro de su paquete?
fuente
python-box
, vea esta respuestaRespuestas:
Hice eso una vez. Al final, encontré mi basicconfig.py simplificado adecuado para mis necesidades. Puede pasar un espacio de nombres con otros objetos para que haga referencia si es necesario. También puede pasar valores predeterminados adicionales de su código. También asigna la sintaxis de estilo de asignación y atributo al mismo objeto de configuración.
fuente
basicconfig.py
archivo al que se hace referencia parece haberse movido a github.com/kdart/pycopia/blob/master/core/pycopia/…ConfigHolder
con un dictado de configuraciones que me gustaría configurar y pasar entre módulos?confit
y admite la fusión de múltiples fuentes. Es parte de un nuevo módulo devtest.config .¿Qué tal si usamos los tipos integrados como este?
Accederá a los valores de la siguiente manera:
Si está dispuesto a sacrificar el potencial de calcular expresiones dentro de su árbol de configuración, puede usar YAML y terminar con un archivo de configuración más legible como este:
y use una biblioteca como PyYAML para analizar y acceder convenientemente al archivo de configuración
fuente
Me gusta esta solución para pequeñas aplicaciones :
Y luego el uso es:
.. te gustará porque:
App
,@property
, pero requiere más código de manejo variable por elemento y está basado en objetos.--Editar-- : Para aplicaciones grandes, almacenar valores en un archivo YAML (es decir, propiedades) y leerlos como datos inmutables es un mejor enfoque (es decir, la respuesta de blubb / ohaal ). Para aplicaciones pequeñas, esta solución anterior es más simple.
fuente
¿Qué tal usar clases?
fuente
Similar a la respuesta de blubb. Sugiero construirlos con funciones lambda para reducir el código. Me gusta esto:
Sin embargo, esto huele como si quisieras hacer una clase.
O, como señaló MarkM, podría usar
namedtuple
fuente
pass
es un nombre de variable desafortunado, ya que también es una palabra clave.mkDict
lambda. Si llamamos a nuestra claseUser
, las claves del diccionario "config" se inicializarían algo así como{'st3v3': User('password','blonde','Steve Booker')}
. Cuando su "usuario" está en unauser
variable, puede acceder a sus propiedades comouser.hair
, etc.User = namedtuple('User', 'passwd hair name'); config = {'st3v3': User('password', 'blonde', 'Steve Booker')}
Una pequeña variación de la idea de Husky que utilizo. Cree un archivo llamado 'globals' (o lo que quiera) y luego defina múltiples clases en él, como tal:
Luego, si tiene dos archivos de código c1.py y c2.py, ambos pueden tener en la parte superior
Ahora todo el código puede acceder y establecer valores, como tales:
La gente olvida que las clases existen, incluso si no se crea una instancia de ningún objeto que sea miembro de esa clase. Y variables en una clase que no están precedidas por "yo". se comparten en todas las instancias de la clase, incluso si no hay ninguna. Una vez que cualquier código cambia "depuración", el resto del código ve el cambio.
Al importarlo como gl, puede tener varios archivos y variables de este tipo que le permiten acceder y establecer valores en archivos de código, funciones, etc., pero sin peligro de colisión de espacios de nombres.
Esto carece de algunas de las comprobaciones inteligentes de errores de otros enfoques, pero es simple y fácil de seguir.
fuente
globals
, ya que es una función incorporada que devuelve un dict con cada símbolo en el ámbito global actual. Además, PEP8 recomienda CamelCase (con todas las mayúsculas en acrónimos) para las clases (es decirDBInfo
) y mayúsculas con guiones bajos para las llamadas constantes (es decirDEBUG
).globals
, el autor debería cambiar el nombreSeamos honestos, probablemente deberíamos considerar el uso de una biblioteca mantenida por Python Software Foundation :
https://docs.python.org/3/library/configparser.html
Ejemplo de configuración: (formato ini, pero JSON disponible)
Ejemplo de código:
Haciéndolo accesible a nivel mundial:
Desventajas:
fuente
por favor, consulte el sistema de configuración de IPython, implementado a través de traitlets para el tipo de aplicación que está haciendo manualmente.
Cortar y pegar aquí para cumplir con las pautas de SO para no solo eliminar enlaces cuando el contenido de los enlaces cambia con el tiempo.
documentación de traitlets
Para lograr esto, básicamente definen 3 clases de objetos y sus relaciones entre sí:
1) Configuración: básicamente un ChainMap / diccionario básico con algunas mejoras para la fusión.
2) Configurable: clase base para crear una subclase de todas las cosas que desea configurar.
3) Aplicación: objeto del que se crea una instancia para realizar una función de aplicación específica, o su aplicación principal para software de propósito único.
En sus palabras:
fuente