2011-06-01 10 views
57

सरल सामग्री को अधिक जटिल बनाने में मेरी अंतहीन खोज में, मैं पाइथन अंडे पैकेज में पाए गए 'config.py' के अंदर वैश्विक कॉन्फ़िगरेशन चर प्रदान करने के लिए सबसे अधिक 'पायथनिक' तरीका खोज रहा हूं।config.py में वैश्विक कॉन्फ़िगरेशन चर प्रदान करने के लिए सबसे पाइथोनिक तरीका?

from config import * 
dbname = MYSQL_DATABASE 
for table in MYSQL_DATABASE_TABLES: 
    print table 

या:

MYSQL_PORT = 3306 
MYSQL_DATABASE = 'mydb' 
MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups'] 

इसलिए वैश्विक चर निम्न तरीकों में से एक में आयात किया जाता है:

परंपरागत तरीके से (! आह, अच्छा ol '#define) इस प्रकार है :

import config 
dbname = config.MYSQL_DATABASE 
assert(isinstance(config.MYSQL_PORT, int)) 

यह समझ में आता है, लेकिन कभी-कभी बी ई थोड़ा गन्दा, खासकर जब आप कुछ चर के नाम याद रखने की कोशिश कर रहे हैं। इसके अलावा, 'कॉन्फ़िगरेशन' ऑब्जेक्ट प्रदान करते हुए, गुणों के साथ गुणों के साथ, अधिक लचीला हो सकता है।

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' 

और तो, bpython config.py फ़ाइल से एक का नेतृत्व लेने, मैं के साथ आया था इस तरह से इस्तेमाल किया जाता है:

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

+0

अक्सर मैं एक JSON कॉन्फ़िगरेशन फ़ाइल लिखना समाप्त करता हूं। इसे पाइथन संरचनाओं में आसानी से पढ़ा जा सकता है और एक उपकरण द्वारा भी बनाया जा सकता है। ऐसा लगता है कि सबसे लचीलापन है और केवल लागत कुछ ब्रेसिज़ हैं जो उपयोगकर्ता को परेशान कर सकती हैं। हालांकि, मैंने कभी अंडे नहीं लिखा था। शायद यह मानक तरीका है। उस मामले में बस ऊपर मेरी टिप्पणी को अनदेखा करें। – erikbwork

+0

आप "self.__ dict __। कुंजी()" – Karlisson

उत्तर

5

मैंने इसे एक बार किया था। आखिर में मैंने अपनी सरलीकृत basicconfig.py मेरी आवश्यकताओं के लिए पर्याप्त पाया। यदि आपको आवश्यकता हो तो संदर्भ के लिए आप अन्य ऑब्जेक्ट्स के साथ नामस्थान में पास कर सकते हैं। आप अपने कोड से अतिरिक्त डिफ़ॉल्ट भी पास कर सकते हैं। यह एक ही कॉन्फ़िगरेशन ऑब्जेक्ट में विशेषता और मानचित्रण शैली वाक्यविन्यास को मानचित्रित करता है।

+4

प्रतीत होता है कि 'basicconfig.py' फ़ाइल को https://github.com/kdart/pycopia/blob/master/core/pycopia/basicconfig.py –

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 पार्स और के blubb का जवाब कॉन्फ़िग फ़ाइल

6

इसी प्रकार के उपयोग करने के लिए। मुझे निर्मित प्रकार पसंद हैं। यदि आप कर सकते हैं तो मैं उन्हें लैम्ब्डा कार्यों के साथ बनाने का सुझाव देता हूं।इस तरह:

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

' पास' एक दुर्भाग्यपूर्ण चर नाम है, क्योंकि यह एक कीवर्ड भी है। – ThS

+0

ओह हाँ ... मैंने अभी इस गूंगा उदाहरण को एक साथ खींचा है। मैं –

+0

नाम बदलूंगा इस तरह के दृष्टिकोण के लिए, आप 'mkDict' lambda के बजाय कक्षा पर विचार कर सकते हैं। अगर हम अपनी कक्षा 'उपयोगकर्ता' कहते हैं, तो आपकी "कॉन्फ़िगरेशन" शब्दकोश कुंजी को '{' st3v3 ': उपयोगकर्ता (' पासवर्ड ',' गोरा ',' स्टीव बुकर ')} जैसे कुछ प्रारंभ किया जाएगा। जब आपका "उपयोगकर्ता" 'उपयोगकर्ता' चर में होता है, तो आप इसके गुणों को' user.hair' आदि के रूप में एक्सेस कर सकते हैं। –

0

कृपया आईपीथॉन कॉन्फ़िगरेशन सिस्टम देखें, जो आपके द्वारा मैन्युअल रूप से किए जा रहे प्रकार प्रवर्तन के लिए ट्रिपलेट्स के माध्यम से लागू किया गया है।

एसओ दिशानिर्देशों का अनुपालन करने के लिए यहां कट और पेस्ट किया गया ताकि लिंक के साथ समय के साथ लिंक की सामग्री में बदलाव न हो। श्रेणीबद्ध कॉन्फ़िगरेशन जानकारी के लिए

समर्थन:

traitlets documentation

यहां मुख्य आवश्यकताओं हमारे पास करने के लिए हमारे विन्यास प्रणाली चाहता था कर रहे हैं।

कमांड लाइन विकल्प पार्सर्स के साथ पूर्ण एकीकरण। अक्सर, आप कॉन्फ़िगरेशन फ़ाइल को पढ़ना चाहते हैं, लेकिन फिर कमांड लाइन विकल्पों वाले कुछ मानों को ओवरराइड करें। हमारी कॉन्फ़िगरेशन सिस्टम इस प्रक्रिया को स्वचालित करता है और कॉन्फ़िगरेशन पदानुक्रम में प्रत्येक कमांड लाइन विकल्प को किसी विशेष विशेषता से लिंक करने की अनुमति देता है जो इसे ओवरराइड करेगा।

कॉन्फ़िगरेशन फ़ाइलें जो स्वयं पाइथन कोड मान्य हैं। यह कई चीजें पूरा करता है। सबसे पहले, आपके कॉन्फ़िगरेशन फ़ाइलों में तर्क डालना संभव हो जाता है जो आपके ऑपरेटिंग सिस्टम, नेटवर्क सेटअप, पायथन संस्करण इत्यादि के आधार पर विशेषताओं को सेट करता है। दूसरा, पाइथन में पदानुक्रमित डेटा संरचनाओं, अर्थात् नियमित विशेषता पहुंच (Foo) तक पहुंचने के लिए एक सुपर सरल वाक्यविन्यास है। Bar.Bam.name)। तीसरा, पायथन का उपयोग करना उपयोगकर्ताओं के लिए कॉन्फ़िगरेशन विशेषताओं को एक कॉन्फ़िगरेशन फ़ाइल से दूसरे में आयात करना आसान बनाता है। चौथा, हालांकि पाइथन गतिशील रूप से टाइप किया गया है, इसमें ऐसे प्रकार होते हैं जिन्हें रनटाइम पर चेक किया जा सकता है। इस प्रकार, एक कॉन्फ़िगरेशन फ़ाइल में 1 पूर्णांक '1' है, जबकि '1' एक स्ट्रिंग है।

रनटाइम पर आवश्यक कक्षाओं में कॉन्फ़िगरेशन जानकारी प्राप्त करने के लिए एक पूरी तरह से स्वचालित विधि। एक विशेष विशेषता निकालने के लिए कॉन्फ़िगरेशन पदानुक्रम चलने वाला कोड लिखना दर्दनाक है। जब आपके पास सैकड़ों विशेषताओं के साथ जटिल कॉन्फ़िगरेशन जानकारी होती है, तो इससे आप रोना चाहते हैं।

टाइपिंग और सत्यापन टाइप करें जो पूरे कॉन्फ़िगरेशन पदानुक्रम को रनटाइम से पहले स्थिर रूप से निर्दिष्ट करने की आवश्यकता नहीं है। पायथन एक बहुत ही गतिशील भाषा है और जब आप कोई प्रोग्राम शुरू होता है तो उसे हमेशा कॉन्फ़िगर करने की आवश्यकता नहीं होती है।

इस प्राप्त करने के लिए वे मूल रूप से एक दूसरे के लिए 3 वस्तु वर्गों और उनके संबंधों को परिभाषित:

1) विन्यास - मूल रूप से एक ChainMap/मर्ज करने के लिए कुछ संवर्द्धन के साथ बुनियादी dict।

2) कॉन्फ़िगर करने योग्य - बेस क्लास उन सभी चीजों को उप-वर्गीकृत करने के लिए जिन्हें आप कॉन्फ़िगर करना चाहते हैं।

3) आवेदन - ऑब्जेक्ट जिसे एक विशिष्ट अनुप्रयोग फ़ंक्शन करने के लिए तत्काल किया जाता है, या एकल उद्देश्य सॉफ़्टवेयर के लिए आपका मुख्य अनुप्रयोग।

उनके शब्दों में:

आवेदन: आवेदन

एक आवेदन एक प्रक्रिया है कि एक विशेष काम करता है।सबसे स्पष्ट अनुप्रयोग ipython कमांड लाइन प्रोग्राम है। प्रत्येक एप्लिकेशन एक या अधिक कॉन्फ़िगरेशन फ़ाइलों और कमांड लाइन विकल्पों का एक सेट पढ़ता है और फिर एप्लिकेशन के लिए एक मास्टर कॉन्फ़िगरेशन ऑब्जेक्ट बनाता है। यह कॉन्फ़िगरेशन ऑब्जेक्ट तब कॉन्फ़िगर करने योग्य ऑब्जेक्ट्स को पास किया जाता है जो एप्लिकेशन बनाता है। ये कॉन्फ़िगर करने योग्य ऑब्जेक्ट्स एप्लिकेशन के वास्तविक तर्क को लागू करते हैं और कॉन्फ़िगरेशन ऑब्जेक्ट को स्वयं कॉन्फ़िगर करने के तरीके को जानते हैं।

अनुप्रयोगों में हमेशा एक लॉग विशेषता है जो कॉन्फ़िगर किया गया लॉगर है। यह केंद्रीकृत लॉगिंग कॉन्फ़िगरेशन प्रति-एप्लिकेशन की अनुमति देता है। कॉन्फ़िगर करने योग्य: कॉन्फ़िगर करने योग्य

कॉन्फ़िगर करने योग्य एक नियमित पायथन क्लास है जो किसी एप्लिकेशन में सभी मुख्य कक्षाओं के लिए बेस क्लास के रूप में कार्य करता है। कॉन्फ़िगर करने योग्य बेस क्लास हल्का वजन है और केवल एक चीज करता है।

यह कॉन्फ़िगर करने योग्य हैसट्रेट्स का एक उप-वर्ग है जो जानता है कि स्वयं को कॉन्फ़िगर कैसे करें। मेटाडेटा कॉन्फ़िगरेशन के साथ क्लास स्तरीय लक्षण = सही मान बनें जिन्हें कमांड लाइन और कॉन्फ़िगरेशन फ़ाइलों से कॉन्फ़िगर किया जा सकता है।

डेवलपर कॉन्फ़िगर करने योग्य उप-वर्ग बनाते हैं जो एप्लिकेशन में सभी तर्क लागू करते हैं। इनमें से प्रत्येक उप-वर्ग की अपनी कॉन्फ़िगरेशन जानकारी होती है जो यह नियंत्रित करती है कि उदाहरण कैसे बनाए जाते हैं।

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

और फिर उपयोग है:

3

मैं छोटे अनुप्रयोगों के लिए इस समाधान चाहते

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 

.. आप इसे पसंद चाहिए क्योंकि:

  • का उपयोग करता है कक्षा variab लेस (कोई वस्तु के आसपास पारित करने के लिए/कोई सिंगलटन) की आवश्यकता,
  • का उपयोग करता है में निर्मित प्रकार समझाया और की तरह (है) App पर एक विधि कॉल लग रहा है,
  • व्यक्ति config अचल स्थिति, पर नियंत्रण है म्यूटेबल ग्लोबल्स सबसे खराब प्रकार के ग्लोबल्स हैं।
  • पारंपरिक बढ़ावा देता है और अच्छी तरह से अपने स्रोत कोड में पहुँच/पठनीयता नामित
  • एक साधारण क्लास है, लेकिन लागू करता है संरचित पहुँच, एक वैकल्पिक @property उपयोग करने के लिए है, लेकिन है कि आइटम प्रति अधिक चर हैंडलिंग कोड की आवश्यकता है और वस्तु है आधारित।
  • में नए कॉन्फ़िगरेशन आइटम जोड़ने और इसकी उत्परिवर्तन सेट करने के लिए न्यूनतम परिवर्तन की आवश्यकता होती है।

--Edit--: बड़े अनुप्रयोगों के लिए, एक 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) 

लोग भूल जाते हैं कक्षाएं मौजूद हैं, भले ही कोई वस्तु कभी instantiated जाता है कि उस वर्ग के एक सदस्य है। और एक वर्ग में वेरिएबल जो 'स्वयं' से पहले नहीं हैं। कक्षा के सभी उदाहरणों में साझा किया जाता है, भले ही कोई भी न हो। एक बार 'डीबग' किसी भी कोड द्वारा बदल दिया जाता है, अन्य सभी कोड परिवर्तन को देखता है।

इसे ग्ल के रूप में आयात करके, आपके पास ऐसी कई फ़ाइलें और चर हो सकती हैं जो आपको कोड फ़ाइलों, फ़ंक्शंस इत्यादि में मूल्यों तक पहुंचने और सेट करने देती हैं, लेकिन नेमस्पेस टकराव का कोई खतरा नहीं है।

इसमें अन्य दृष्टिकोणों की कुछ चालाक त्रुटि जांच की कमी है, लेकिन यह सरल और अनुसरण करने में आसान है।

संबंधित मुद्दे