config.pyでグローバル構成変数を提供する最もPython的な方法?[閉まっている]


98

非常に複雑な単純なものの無限の探求で、Python eggパッケージにある典型的な「config.py」内にグローバル構成変数を提供するための最も「Python的な」方法を研究しています。

伝統的な方法(ああ、古き良き#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))

これは理にかなっていますが、特に特定の変数の名前を覚えようとする場合は、少し面倒になることがあります。さらに、変数を属性として持つ「設定」オブジェクトを提供すると、より柔軟になる可能性があります。それで、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))

これは、パッケージ内でグローバル変数を格納およびフェッチするための、より読みやすく、表現力があり、柔軟な方法のようです。

これまでで最も大きなアイデア?これらの状況に対処するためのベストプラクティスは何ですか?パッケージ内のグローバル名と変数を格納およびフェッチする方法は何ですか?


3
あなたはすでにここで良いかそうでないかを決定しました。構成自体は、JSON、XML、* nixesとWindowsのさまざまな文法など、さまざまな方法で格納できます。構成ファイルを書く人(ツール、人間、背景など)によっては、異なる文法が望ましい場合があります。ほとんどの場合、configファイルをプログラムで使用するのと同じ言語で作成することは、ユーザーに非常に大きな力を与えるため、良い考えではないかもしれません(自分自身かもしれませんが、自分ができるすべてを覚えていないかもしれません)数か月先に失敗します)。
erikbwork

4
多くの場合、JSON構成ファイルを作成します。Python構造に簡単に読み込むことができ、ツールで作成することもできます。最も柔軟性があるようで、唯一のコストは、ユーザーにとって煩わしいブレースです。私は卵を書いたことはありません。多分それが標準的な方法です。その場合は、上記のコメントを無視してください。
erikbwork

1
「self .__ dict __。keys()」の代わりに「vars(self)」を使用できます
Karlisson

1
Pythonで設定ファイルを使用するベストプラクティス何ですか?彼らは、「多くの方法が可能で、バイクシェッドスレッドがすでに存在しています。セキュリティを気にしない限り、config.pyは適切です。」と答えます。
Nikana Reklawyks

私は結局を使用してしまいました。python-boxこの回答を
進化

回答:


5

一度やった。結局、私は自分の単純化されたbasicconfig.pyが自分のニーズに適していると感じました。必要に応じて、参照する他のオブジェクトとともに名前空間を渡すことができます。コードから追加のデフォルトを渡すこともできます。また、属性とマッピングスタイルの構文を同じ構成オブジェクトにマップします。


6
basicconfig.pyファイルには、に移動しているようだと呼ばgithub.com/kdart/pycopia/blob/master/core/pycopia/...
ポール・M Furley

私はこれが数年前であることを知っていますが、私は初心者であり、この構成ファイルは本質的に私が探しているものだと思います(多分高度すぎるかもしれません)。もっとよく理解したいと思います。ConfigHolderモジュール間で設定して渡したいコンフィグのディクテーションで初期化を渡すだけですか?
Jinx

@Jinxこの時点で、設定にYAMLファイルとPyYAMLを使用します(現在使用しています)。また、サードパーティのモジュールを使用し、confit複数のソースのマージをサポートしています。新しいdevtest.configモジュールの一部です。
キース

56

このような組み込み型を使用するのはどうですか?

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のようなライブラリを使用して、便利に構成ファイルを解析してアクセスします


ただし、通常は異なる構成ファイルを使用する必要があるため、コード内に構成データはありません。したがって、「config」は、外部のJSON / YAMLファイルであり、すべての単一クラスで、アクセスするたびにディスクからロードする必要があります。問題は「一度ロードする」ことであり、ロードされたデータにグローバルにアクセスすることです。あなたが提案したソリューションでそれをどのように行いますか?
masi

3
データをメモリに保持するために何かが存在する場合^^
シンタティック

16

は小さなアプリケーションのためのこのソリューション好きです

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

..次の理由で、それを気に入ってください。

  • クラス変数を使用する(渡すオブジェクトがない/シングルトンが不要)、
  • 用途は、カプセル化された組み込み型と上の(ある)メソッド呼び出しのように見えApp
  • 個々の構成の不変性を制御でき、可変グローバルは最悪の種類のグローバルです。
  • ソースコードで従来の名前の付いたアクセス/読みやすさを促進する
  • 単純なクラスですが、構造化アクセスを適用します。代わりにを使用することもできますが@property、アイテムごとにさらに多くの変数処理コードが必要で、オブジェクトベースです。
  • 新しい構成項目を追加し、その可変性を設定するための最小限の変更が必要です

-編集 -:大規模なアプリケーションの場合、値をYAML(つまり、プロパティ)ファイルに格納し、それを不変データとして読み取ることをお勧めします(つまり、blubb / ohaalの答え)。小さなアプリケーションの場合、上記のこのソリューションはより簡単です。


9

クラスを使うのはどうですか?

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

# main.py
from config import MYSQL

print(MYSQL.PORT) # 3306

8

blubbの答えに似ています。コードを削減するために、ラムダ関数を使用してビルドすることをお勧めします。このような:

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

しかし、これはあなたがクラスを作りたいと思うかもしれないようなにおいがしません。

または、MarkMが述べたように、 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

3
passはキーワードでもあるため、残念な変数名です。
Thomas Schreiter 2014

そうそう…私はこのばかげた例をまとめただけだ。私は名前を変更します
Cory-G 14

この種のアプローチでは、mkDictラムダの代わりにクラスを検討するかもしれません。クラスを呼び出すUserと、「config」辞書キーはのように初期化されます{'st3v3': User('password','blonde','Steve Booker')}。あなたの「ユーザーが」である場合にはuser、変数、あなたがして、そのプロパティにアクセスすることができますuser.hairなど、
アンドリュー・パーマー

このスタイルが気に入ったら、collections.namedtupleを使用することもできます。 User = namedtuple('User', 'passwd hair name'); config = {'st3v3': User('password', 'blonde', 'Steve Booker')}
MarkM 2018年

7

私が使用するハスキーのアイデアの小さなバリエーション。'globals'(または好きなもの)というファイルを作成し、その中に複数のクラスを定義します。

#globals.py

class dbinfo :      # for database globals
    username = 'abcd'
    password = 'xyz'

class runtime :
    debug = False
    output = 'stdio'

次に、2つのコードファイルc1.pyとc2.pyがある場合、どちらも先頭に配置できます。

import globals as gl

これで、すべてのコードが値にアクセスして設定できるようになります。

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

そのクラスのメンバーであるオブジェクトがインスタンス化されていない場合でも、人々はクラスを忘れています。また、「self」が前に付いていないクラスの変数。クラスがない場合でも、クラスのすべてのインスタンスで共有されます。「デバッグ」がコードによって変更されると、他のすべてのコードが変更を認識します。

glとしてインポートすることで、コードファイルや関数などにまたがって値にアクセスして設定できるようなファイルや変数を複数持つことができますが、名前空間の衝突の危険はありません。

これは他のアプローチの巧妙なエラーチェックのいくつかを欠いていますが、シンプルで簡単に追跡できます。


1
モジュールの名前globalsは、現在のグローバルスコープ内のすべてのシンボルとともにdictを返す組み込み関数であるため、誤解されます。さらに、PEP8はクラス(つまりDBInfo)にはキャメルケース(頭字語はすべて大文字)を、いわゆる定数(つまりDEBUG)にはアンダースコア付きの大文字を推奨しています。
ヌーノ・アンドレ

1
コメントについて@NunoAndréに感謝します。私がそれを読むまで、私はこの答えがで何かおかしいと思っていましたglobals。著者は名前を変更する必要があります
oglop

このアプローチは私の行くところです。しかし、人々が言うことの多くは「最高」のアプローチだと思います。このようにconfig.pyを実装することのいくつかの欠点を述べることができますか?
Yash Nag

5

正直言って、Python Software Foundationが保守するライブラリの使用を検討する必要があります。

https://docs.python.org/3/library/configparser.html

構成例:(ini形式ですが、JSONを使用できます)

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

コード例:

>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.read('example.ini')
>>> config['DEFAULT']['Compression']
'yes'
>>> config['DEFAULT'].getboolean('MyCompression', fallback=True) # get_or_else

グローバルにアクセスできるようにする:

import configpaser
class App:
 __conf = None

 @staticmethod
 def config():
  if App.__conf is None:  # Read only once, lazy.
   App.__conf = configparser.ConfigParser()
   App.__conf.read('example.ini')
  return App.__conf

if __name__ == '__main__':
 App.config()['DEFAULT']['MYSQL_PORT']
 # or, better:
 App.config().get(section='DEFAULT', option='MYSQL_PORT', fallback=3306)
 ....

欠点:

  • 制御されていないグローバルな可変状態。

他のファイルのifステートメントを適用して構成を変更する必要がある場合は、.iniファイルを使用しても意味がありません。代わりにconfig.pyを使用することをお勧めしますが、値が変更されず、呼び出して使用するだけの場合は、.iniファイルの使用に同意します。
Kourosh

3

手動で実行しているタイプの強制に対してトレイトレットを介して実装されたIPython設定システムを確認してください。

リンクのコンテンツが時間の経過とともに変化するときにリンクをドロップするだけでなく、SOガイドラインに準拠するためにここで切り取って貼り付けます。

トレイトのドキュメント

構成システムに必要な主な要件は次のとおりです。

階層構成情報のサポート。

コマンドラインオプションパーサーとの完全な統合。多くの場合、構成ファイルを読み取ってから、コマンドラインオプションで一部の値を上書きします。私たちの構成システムは、このプロセスを自動化し、各コマンドラインオプションを、それが上書きする構成階層の特定の属性にリンクできるようにします。

それ自体が有効なPythonコードである構成ファイル。これは多くのことを達成します。1つ目は、オペレーティングシステム、ネットワーク設定、Pythonバージョンなどに基づいて属性を設定するロジックを構成ファイルに含めることが可能になることです。2つ目は、Pythonには階層データ構造にアクセスするための非常に単純な構文、つまり通常の属性アクセス(Foo。 Bar.Bam.name)。3番目に、Pythonを使用すると、ユーザーが1つの構成ファイルから別の構成ファイルに構成属性を簡単にインポートできるようになります。第4に、Pythonは動的に型付けされていますが、実行時にチェックできる型があります。したがって、設定ファイルの1は整数「1」であり、「1」は文字列です。

構成情報を実行時に必要とするクラスに取得するための完全に自動化された方法。特定の属性を抽出するために構成階層をたどるコードを書くのは大変です。何百もの属性を持つ複雑な構成情報がある場合、これは泣きたくなるでしょう。

実行前に静的に構成階層全体を指定する必要のない型チェックと検証。Pythonは非常に動的な言語であり、プログラムの起動時に構成する必要があるすべてを常に知っているとは限りません。

これを実現するために、基本的に3つのオブジェクトクラスとそれらの相互関係を定義します。

1)構成-基本的には、ChainMap /マージのためのいくつかの拡張機能を備えた基本的な辞書。

2)構成可能-構成したいすべてのものをサブクラス化する基本クラス。

3)アプリケーション-特定のアプリケーション機能を実行するためにインスタンス化されるオブジェクト、または単一目的のソフトウェアのメインアプリケーション。

彼らの言葉で:

アプリケーション:アプリケーション

アプリケーションは、特定のジョブを実行するプロセスです。最も明白なアプリケーションは、ipythonコマンドラインプログラムです。各アプリケーションは、1つ以上の構成ファイルと単一のコマンドラインオプションセットを読み取り、アプリケーションのマスター構成オブジェクトを生成します。次に、この構成オブジェクトは、アプリケーションが作成する構成可能オブジェクトに渡されます。これらの設定可能なオブジェクトは、アプリケーションの実際のロジックを実装し、設定オブジェクトを指定して自分自身を設定する方法を知っています。

アプリケーションには常に、構成されたロガーであるログ属性があります。これにより、アプリケーションごとにログを集中的に構成できます。構成可能:構成可能

構成可能は、アプリケーションのすべてのメインクラスの基本クラスとして機能する通常のPythonクラスです。Configurable基本クラスは軽量で、1つのことだけを行います。

このConfigurableは、それ自体を構成する方法を知っているHasTraitsのサブクラスです。メタデータconfig = Trueを持つクラスレベルの特性は、コマンドラインと構成ファイルから構成できる値になります。

開発者は、アプリケーションのすべてのロジックを実装する構成可能なサブクラスを作成します。これらの各サブクラスには、インスタンスの作成方法を制御する独自の構成情報があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.