大多数Pythonic的方式来提供config.py中的全局configurationvariables?
在对过于复杂的简单的东西的无尽追求中,我正在研究Python的蛋包中典型的“ config.py ”中提供全局configurationvariables的最“Python”方法。
传统的方式(aah,good ol #define !)如下:
MYSQL_PORT = 3306 MYSQL_DATABASE = 'mydb' MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
因此全局variables以下列方式之一导入:
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))
这是有道理的,但有时可能会有点混乱,特别是当你想记住某些variables的名字。 此外,提供一个“configuration”对象 ,以variables作为属性 ,可能会更加灵活。 所以,从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))
这似乎是一个更具可读性,expression性和灵活性的方式来存储和获取包内的全局variables。
最温和的想法? 处理这些情况的最佳做法是什么? 什么是你的方式来存储和获取您的包内的全局名称和variables?
我曾经这样做过。 最终,我发现我的简化的basicconfig.py足以满足我的需求。 你可以传入一个名字空间和其他对象,以便在需要的时候进行引用。 你也可以从你的代码中传入更多的默认值。 它还将属性和映射样式语法映射到相同的configuration对象。
如何使用像这样的内置types:
config = { "mysql": { "user": "root", "pass": "secret", "tables": { "users": "tb_users" } # etc } }
您可以按如下方式访问这些值:
config["mysql"]["tables"]["users"]
如果你愿意牺牲在你的configuration树中计算expression式的可能性,你可以使用YAML,并得到一个更可读的configuration文件,如下所示:
mysql: - user: root - pass: secret - tables: - users: tb_users
并使用PyYAML之类的库进行parsing并访问configuration文件
类似于blubb的回答。 我喜欢内置的types。 如果可以的话,我build议用lambda函数来构build它们。 喜欢这个:
mkDict = lambda passwd, hair, name: {'passwd':passwd, 'hair':hair, 'name':name} #Col Names: Password Hair Color Real Name config = {'st3v3' : mkDict('password', 'blonde', 'Steve Booker'), 'blubb' : mkDict('12345678', 'black', 'Bubb Ohaal'), 'suprM' : mkDict('kryptonite', 'black', 'Clark Kent'), #... }
耶,现在你不必复制粘贴这么多。 有了评论,以后比较和读取数据也更容易。
如何使用类?
# config.py class MYSQL: PORT = 3306 DATABASE = 'mydb' DATABASE_TABLES = ['tb_users', 'tb_groups'] # main.py from config import MYSQL print(MYSQL.PORT) # 3306
我喜欢这个小应用程序的解决scheme:
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
..你应该喜欢它,因为:
- 使用类variables (没有对象传递/不需要单例),
- 使用封装的内置types ,看起来像是在
App
调用方法, - 控制个人configuration不变性 , 可变全局variables是最糟糕的全局variables 。
- 在您的源代码中促进传统和有名的访问/可读性
- 是一个简单的类,但强制结构化的访问 ,另一种方法是使用
@property
,但是这需要更多的variables处理代码每个项目,是基于对象的。 - 只需要很less的修改来添加新的configuration项并设置其可变性。
– 编辑 – 对于大型应用程序,将值存储在YAML(即属性)文件中,并以不可变数据读取是更好的方法(即blubb / ohaal的答案 )。 对于小型应用,上述解决scheme更简单。
请查看IPythonconfiguration系统,通过traitlets实现您正在执行的types强制实施。
在这里剪切和粘贴,以符合SO指导原则,不仅仅是随着链接的内容随着时间的推移而丢弃链接。
traitlets文件
以下是我们希望configuration系统具备的主要要求:
支持分层configuration信息。
与命令行选项parsing器完全集成。 通常,您要读取configuration文件,但是用命令行选项覆盖一些值。 我们的configuration系统自动执行这个过程,并允许每个命令行选项被链接到configuration层次结构中的一个特定的属性,它将被覆盖。
configuration文件本身是有效的Python代码。 这完成了很多事情。 首先,可以将configuration文件中的逻辑放在基于操作系统,networking设置,Python版本等设置属性上。其次,Python对于访问分层数据结构(即常规属性访问(Foo。 Bar.Bam.name)。 第三,使用Python可以使用户轻松地将configuration属性从一个configuration文件导入到另一个。 第四,即使Python是dynamictypes的,它的types也可以在运行时检查。 因此,configuration文件中的1是整数“1”,而“1”是一个string。
一种完全自动化的方法,用于在运行时将configuration信息提供给需要它的类。 编写遍历configuration层次结构来提取特定属性的代码是很痛苦的。 当你拥有数百个属性的复杂configuration信息时,这让你想哭。
types检查和validation不需要在运行前静态指定整个configuration层次结构。 Python是一种非常dynamic的语言,当程序启动时,并不总是知道需要configuration的所有东西。
为了实现这个目标,他们基本上定义了三个对象类和它们之间的关系:
1)configuration – 基本上是一个ChainMap /基本字典,有一些增强合并。
2)可configuration的 – 基类来inheritance你想要configuration的所有东西。
3)应用程序 – 实例化的对象,执行特定的应用程序function,或主要应用程序的单一用途的软件。
用他们的话说:
应用:应用
应用程序是执行特定工作的过程。 最明显的应用是ipython命令行程序。 每个应用程序读取一个或多个configuration文件和一组命令行选项,然后为该应用程序生成一个主configuration对象。 然后将此configuration对象传递给应用程序创build的可configuration对象。 这些可configuration对象实现了应用程序的实际逻辑,并知道如何根据configuration对象来configuration自己。
应用程序始终具有一个configuration的logging器的日志属性。 这允许每个应用程序的集中日志configuration。 可configuration:可configuration
一个可configuration的是一个普通的Python类,作为应用程序中所有主类的基类。 可configuration的基类是轻量级的,只做一件事情。
这个可configuration是HasTraits的一个子类,知道如何configuration自己。 具有metadata config = True的级别特征成为可以从命令行和configuration文件configuration的值。
开发人员创build实现应用程序中所有逻辑的可configuration子类。 这些子类中的每一个都有自己的configuration信息来控制如何创build实例。
赫斯基的想法,我使用的一个小的变化。 创build一个名为“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)
人们忘记了类的存在,即使没有实例化对象是该类的成员。 而在一个没有“自我”之前的类中的variables。 在class级的所有实例中共享,即使没有。 一旦“debugging”被任何代码改变,所有其他代码就会看到改变。
通过导入它,你可以有多个这样的文件和variables,让你访问和设置代码文件,函数等值,但没有名称空间冲突的危险。
这缺乏其他方法的一些聪明的错误检查,但很简单,容易遵循。