2011-06-01 85 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,你可以有多個這樣的文件和變量可以訪問和跨代碼文件,功能等設定值,但沒有命名空間衝突的危險。

這缺少一些其他方法巧妙錯誤檢查的,但簡單,易於遵循。