2011-06-01 19 views
57

在我对过于复杂简单的东西的无尽追求中,我正在研究Python中最常见的Python提供全局配置变量的方法,这些变量位于Python egg packages中的典型'config.py'中。在config.py中提供全局配置变量的大多数Pythonic方法?

传统的方式(!啊哈,好醇” 的#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)) 

它是有道理的,但有时可以b有点混乱,特别是当你试图记住某些变量的名字时。此外,提供'配置'对象变量作为属性,可能会更灵活。因此,采取从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:%[email protected]%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)) 

这似乎是一个更具可读性,表达性和灵活性的方式来存储和获取包内的全局变量。

有史以来最蠢的想法?应对这些情况的最佳做法是什么?什么是你的存储和获取你的包内的全局名称和变量的方式?

+0

你已经在这里做了一个决定,可能会或可能不会好。配置本身可以以不同的方式存储,比如JSON,XML,用于* nixes和Windows的不同语法等等。根据谁写配置文件(一个工具,一个人,什么背景?),不同的语法可能更可取。大多数情况下,让配置文件使用与您的程序相同的语言来编写配置文件可能不是一个好主意,因为它会给用户带来太多的权力(可能是您自己,但您自己可能不记得所有可以使用的东西在未来几个月出错)。 – erikbwork 2011-06-01 08:53:07

+0

通常我最终会写一个JSON配置文件。它可以轻松地读入python结构,也可以通过工具创建。它似乎具有最大的灵活性,唯一的代价是可能会让用户烦恼的一些大括号。不过,我从来没有写过鸡蛋。也许这是标准方式。在这种情况下,请忽略我上面的评论。 – erikbwork 2011-06-01 08:55:40

+0

您可以使用“瓦尔(个体经营)”,而不是“自我.__字典__。键()” – Karlisson 2013-12-18 15:19:02

回答

5

我做了一次。最终,我发现我的简化basicconfig.py足以满足我的需求。如果需要,您可以传入其他对象的名称空间以供它参考。您也可以传入代码中的其他默认值。它还将属性和映射样式语法映射到相同的配置对象。

+4

的'basicconfig.py'文件提及,似乎已经转移到https://github.com/kdart/pycopia/blob/master/core/pycopia/basicconfig.py – 2015-03-27 11:37:06

43

如何只使用内置的类型是这样的:

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

你会访问值如下:

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

如果你愿意牺牲的潜力为了计算你的配置树中的表达式,你可以使用YAML,最后得到一个更可读的配置文件,如下所示:

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

,并使用像PyYAML库来conventiently解析和访问配置文件

6

类似blubb的答案。我喜欢内置的类型。如果可以的话,我建议用lambda函数来构建它们。像这样:

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'), 
      #... 
     } 

耶,现在你不必复制粘贴这么多。有了评论,以后比较和读取数据也更容易。

+2

'pass'是一个不幸的变量名,因为它也是一个关键字。 – ThS 2014-12-17 00:21:15

+0

噢,我只是把这个愚蠢的例子放在一起。我将更改名称 – 2014-12-18 18:09:13

+0

对于这种方法,您可能会考虑使用类而不是'mkDict' lambda。如果我们调用我们的类User,那么你的“config”字典键将被初始化为'{'st3v3':User('password','blonde','Steve Booker')}'。当你的“用户”在一个“用户”变量中时,你可以通过user.hair等来访问它的属性。 – 2017-03-29 15:22:51

0

请查看IPython配置系统,通过traitlets实现您正在执行的类型强制实施。

在这里剪切和粘贴,以符合SO指导原则,不仅仅是随着链接内容随时间推移而丢弃链接。

traitlets documentation

这是我们希望我们的配置系统具有的主要要求:

支持分层配置信息。

与命令行选项解析器完全集成。通常情况下,您想要读取配置文件,但通过命令行选项覆盖一些值。我们的配置系统自动执行此过程,并允许将每个命令行选项链接到配置层次结构中将覆盖的特定属性。

配置文件本身是有效的Python代码。这完成了很多事情。首先,将配置文件中的逻辑放入基于操作系统,网络设置,Python版本等设置属性成为可能。其次,Python具有访问分层数据结构的超级简单语法,即常规属性访问(Foo。 Bar.Bam.name)。第三,使用Python可以让用户轻松地将配置属性从一个配置文件导入到另一个。第四,尽管Python是动态类型的,但它的类型可以在运行时检查。因此,配置文件中的1是整数'1',而'1'是字符串。

一种用于在运行时将配置信息获取到需要它的类的完全自动化方法。编写遍历配置层次结构以提取特定属性的代码是很痛苦的。当你拥有数百个属性的复杂配置信息时,这让你想哭。

类型检查和验证不需要在运行前静态指定整个配置层次结构。 Python是一种非常动态的语言,当程序启动时,你并不总是知道需要配置的所有东西。

为了达致这他们基本上限定3对象类和它们的关系相互:

1)配置 - 基本上ChainMap /基本与一些增强用于合并字典。

2)可配置 - 基类,用于子类所有你想配置的东西。

3)应用程序 - 被实例化来执行特定应用程序功能的对象,或者您的主要应用程序的单一用途软件。

用他们的话说:

应用:应用

的应用程序是一个过程,做了具体的工作。最明显的应用是ipython命令行程序。每个应用程序读取一个或多个配置文件和一组命令行选项,然后为该应用程序生成一个主配置对象。然后将此配置对象传递给应用程序创建的可配置对象。这些可配置对象实现了应用程序的实际逻辑,并知道如何根据配置对象进行自我配置。

应用程序始终具有一个配置的记录器的日志属性。这允许按应用程序进行集中式日志配置。 可配置:可配置

可配置的是一个常规的Python类,它用作应用程序中所有主类的基类。可配置的基类是轻量级的,只能做一件事。

这个可配置是HasTraits的子类,知道如何配置自己。元数据配置= True的类级别特征成为可以从命令行和配置文件配置的值。

开发人员创建实现应用程序中所有逻辑的可配置子类。这些子类中的每一个都有自己的配置信息,用于控制如何创建实例。

3

我喜欢这个解决方案用于小型应用

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 

..你要喜欢它,因为:

  • 用途class variab LES(没有对象以绕过/无需单),
  • 使用封装内置类型和看起来像(是)上App一个方法调用,
  • 具有超过个别配置不变性控制可变全局变量是最差的全局变量
  • 促进常规和以及命名访问/可读性在源代码中
  • 简单类,但强制结构化访问,一个替代方案是使用@property,但需要更多的可变处理代码每个项目和为对象基。
  • 需要最小的变化添加新的配置项目,并设置其可变性。

- 编辑 -: 对于大型应用程序,在存储YAML值(即属性)文件,并读取在作为不可变的数据是一个较好的方法(即blubb/ohaal's answer)。 对于小型应用,上述解决方案更简单。

5

如何使用类?

# config.py 
class MYSQL: 
    PORT = 3306 
    DATABASE = 'mydb' 
    DATABASE_TABLES = ['tb_users', 'tb_groups'] 

# main.py 
from config import MYSQL 

print(MYSQL.PORT) # 3306 
1

赫斯基的想法,我使用的一个小变化。建立一个叫做“全局”文件(或任何你喜欢的),然后在其中定义多个类,例如:

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

人们忘记阶级存在,即使没有对象已经被实例就是该类的成员。以及不在“自我”之前的类中的变量。在班级的所有实例中共享,即使没有任何实例。一旦“调试”被任何代码改变,所有其他代码就会看到这个改变。

通过导入它作为GL,你可以有多个这样的文件和变量可以访问和跨代码文件,功能等设定值,但没有命名空间冲突的危险。

这缺少一些其他方法巧妙错误检查的,但简单,易于遵循。