variable programming nonlocal modify inside def custom global-variables python config egg

global-variables - programming - use global variable python



La forma más pitonica de proporcionar variables de configuración global en config.py? (7)

¿Qué hay de usar las clases?

# config.py class MYSQL: PORT = 3306 DATABASE = ''mydb'' DATABASE_TABLES = [''tb_users'', ''tb_groups''] # main.py from config import MYSQL print(MYSQL.PORT) # 3306

En mi interminable búsqueda de cosas simples que complican demasiado, estoy investigando la forma más ''pitónica'' de proporcionar variables de configuración globales dentro del típico '' config.py '' que se encuentra en los paquetes de Python.

La forma tradicional (aah, good ol '' #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 maneras:

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 desordenado, especialmente cuando intenta recordar los nombres de ciertas variables. Además, proporcionar un objeto ''configuración'' , con variables como atributos , podría ser más flexible. Entonces, tomando una ventaja 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 lee de la siguiente manera:

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 cual parece una forma más legible, expresiva y flexible de almacenar y obtener variables globales dentro de un paquete.

La peor idea alguna vez? ¿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?


¿Qué tal si usamos los tipos integrados como este?

config = { "mysql": { "user": "root", "pass": "secret", "tables": { "users": "tb_users" } # etc } }

Tendrás acceso a los valores de la siguiente manera:

config["mysql"]["tables"]["users"]

Si está dispuesto a sacrificar el potencial para calcular expresiones dentro de su árbol de configuración, podría usar YAML y terminar con un archivo de configuración más legible como este:

mysql: - user: root - pass: secret - tables: - users: tb_users

y use una biblioteca como PyYAML para analizar y acceder PyYAML al archivo de configuración


Lo hice una vez. Finalmente, encontré mi basicconfig.py simplificado adecuado para mis necesidades. Puede pasar en un espacio de nombres con otros objetos para que lo haga referencia si lo necesita. También puede pasar los valores predeterminados adicionales de su código. También asigna la sintaxis del estilo de asignación y de atributo al mismo objeto de configuración.


Me gusta esta solución para pequeñas aplicaciones :

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")

Y luego el uso es:

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

.. te gustará porque:

  • usa variables de clase (no hay objeto para pasar / no se requiere singleton),
  • utiliza tipos incorporados encapsulados y parece (es) una llamada a un método en la App ,
  • tiene control sobre la inmutabilidad de la configuración individual, los globales mutables son el peor tipo de globales .
  • promueve el acceso / legibilidad convencional y bien nombrado en su código fuente
  • es una clase simple pero impone acceso estructurado , una alternativa es usar @property , pero eso requiere más código de manejo variable por artículo y está basado en objetos.
  • requiere cambios mínimos para agregar nuevos elementos de configuración y establecer su mutabilidad.

- Editar - : para aplicaciones grandes, almacenar valores en un archivo YAML (es decir, propiedades) y leer eso 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.


Similar a la respuesta de blubb. Sugiero construirlos con funciones lambda para reducir el código. Me gusta esto:

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

Aunque huele como si quisieras hacer una clase.

O, como señaló MarkM, podrías usar 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


Una pequeña variación en la idea de Husky que uso. Crea un archivo llamado ''globales'' (o lo que quieras) y luego define múltiples clases en él, como tal:

#globals.py class dbinfo : # for database globals username = ''abcd'' password = ''xyz'' class runtime : debug = False output = ''stdio''

Entonces, si tiene dos archivos de código c1.py y c2.py, ambos pueden tener en la parte superior

import globals as gl

Ahora todo el código puede acceder y establecer valores, como tal:

gl.runtime.debug = False print(gl.dbinfo.username)

Las personas olvidan que existen clases, incluso si nunca se crea una instancia de un objeto que sea miembro de esa clase. Y variables en una clase que no están precedidas por ''self''. se comparten en todas las instancias de la clase, incluso si no hay ninguna. Una vez que "código de depuración" es cambiado por cualquier código, el resto del código ve el cambio.

Al importarlo como gl, puede tener múltiples archivos y variables de este tipo que le permiten acceder y establecer valores entre archivos de código, funciones, etc., pero sin peligro de colisión de espacio de nombres.

Esto carece de la verificación inteligente de errores de otros enfoques, pero es simple y fácil de seguir.


echa un vistazo al sistema de configuración de IPython, implementado a través de traitlets para la ejecución de tipo que estás haciendo manualmente.

Cortado y pegado aquí para cumplir con las pautas de SO, no solo para eliminar enlaces, ya que el contenido de los enlaces cambia con el tiempo.

documentación de traitlets

Estos son los requisitos principales que queríamos que tu sistema de configuración tuviera:

Soporte para información de configuración jerárquica.

Integración completa con analizadores de opciones de línea de comando. A menudo, desea leer un archivo de configuración, pero luego anular algunos de los valores con opciones de línea de comando. Nuestro sistema de configuración automatiza este proceso y permite que cada opción de línea de comando se vincule a un atributo particular en la jerarquía de configuración que prevalecerá.

Archivos de configuración que son en sí mismos código de Python válido. Esto logra muchas cosas. En primer lugar, es posible poner lógica en sus archivos de configuración que establece atributos basados ​​en su sistema operativo, configuración de red, versión de Python, etc. Segundo, Python tiene una sintaxis súper simple para acceder a estructuras de datos jerárquicas, concretamente el acceso a atributos regulares (Foo. Bar.Bam.name). En tercer lugar, el uso de Python facilita a los usuarios la importación de atributos de configuración de un archivo de configuración a otro. En cuarto lugar, aunque Python está tipeado dinámicamente, tiene tipos que se pueden verificar en tiempo de ejecución. Por lo tanto, un 1 en un archivo de configuración es el número entero ''1'', mientras que un ''1'' es una cadena.

Un método totalmente automático para obtener la información de configuración de las clases que lo necesitan en tiempo de ejecución. Escribir código que recorre una jerarquía de configuración para extraer un atributo en particular es doloroso. Cuando tiene información de configuración compleja con cientos de atributos, esto hace que desee llorar.

Verificación y validación de tipos que no requieren que toda la jerarquía de configuración se especifique estáticamente antes del tiempo de ejecución. Python es un lenguaje muy dinámico y no siempre se sabe todo lo que se debe configurar cuando se inicia un programa.

Para lograr esto, básicamente definen 3 clases de objetos y sus relaciones entre sí:

1) Configuración - básicamente un mapa de Cadena / dict básico con algunas mejoras para la fusión.

2) Configurable: clase base para subclasificar todo lo que desea configurar.

3) Aplicación: objeto 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:

Aplicación: aplicación

Una aplicación es un proceso que hace un trabajo específico. La aplicación más obvia es el programa de línea de comando ipython. Cada aplicación lee uno o más archivos de configuración y un único conjunto de opciones de línea de comandos y luego produce un objeto de configuración maestro para la aplicación. Este objeto de configuración se pasa luego a los objetos configurables que crea la aplicación. Estos objetos configurables implementan la lógica real de la aplicación y saben cómo configurarse dado el objeto de configuración.

Las aplicaciones siempre tienen un atributo de registro que es un Registrador configurado. Esto permite una configuración de registro centralizada por aplicación. Configurable: Configurable

Un configurable es una clase regular de Python que sirve como una clase base para todas las clases principales en una aplicación. La clase base Configurable es liviana y solo hace una cosa.

Este Configurable es una subclase de HasTraits que sabe cómo configurarse. Los rasgos de nivel de clase con los metadatos config = True se convierten en valores que se pueden configurar desde la línea de comando y los archivos de configuración.

Los desarrolladores crean subclases configurables que implementan toda la lógica en la aplicación. Cada una de estas subclases tiene su propia información de configuración que controla cómo se crean las instancias.