web-dev-qa-db-ja.com

config.pyでグローバル構成変数を提供する最もPython的な方法は?

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

従来の方法(aah、good ol '#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))

それは理にかなっていますが、特に特定の変数の名前を思い出そうとしている場合は、少し厄介な場合があります。また、'configuration' object変数としての属性とともに指定すると、より柔軟になります。そこで、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))

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

これまでで最も小さなアイデアですか?これらの状況に対処するためのベストプラクティスは何ですか? yourパッケージ内のグローバル名と変数を保存および取得する方法とは何ですか?

77
Rigel Di Scala

一度やった。最終的には、単純化された basicconfig.py がニーズに十分であることがわかりました。必要に応じて、参照する他のオブジェクトとともにネームスペースを渡すことができます。コードから追加のデフォルトを渡すこともできます。また、属性とマッピングスタイルの構文を同じ構成オブジェクトにマップします。

5
Keith

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

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

50
blubb

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
9
Cory-G

私はこのソリューションが好きです小規模アプリケーション用

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 __== "__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のメソッド呼び出しのようです(is)
  • 個々の設定を制御します不変性可変グローバルは最悪の種類のグローバルです。
  • ソースコードで従来の名前付きアクセス/読みやすさを促進します
  • 単純なクラスですが、構造化されたアクセスを強制します、代わりに@property、しかし、それはアイテムごとにより多くの変数処理コードを必要とし、オブジェクトベースです。
  • 最小限の変更が必要です新しい構成アイテムを追加し、その可変性を設定します。

-編集-:大規模なアプリケーションの場合、YAML(つまりプロパティ)ファイルに値を保存し、それを不変データとして読み込むのがより良い方法です(すなわち blubb/ohaalの答え )。小さなアプリケーションの場合、上記のこのソリューションはより簡単です。

8
pds

クラスを使用してはどうですか?

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

# main.py
from config import MYSQL

print(MYSQL.PORT) # 3306
5
Husky

私が使用するハスキーのアイデアの小さなバリエーション。 '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としてインポートすることで、コードファイルや関数などで値にアクセスして設定できる複数のファイルや変数を使用できますが、名前空間の衝突の危険はありません。

これには、他のアプローチの巧妙なエラーチェックの一部が欠けていますが、簡単で従うのは簡単です。

5
eSurfsnake

手動で実行している型の強制のために、トレイレット経由で実装されたIPython構成システムをチェックしてください。

リンクのコンテンツが時間とともに変化するため、リンクをドロップするだけではなく、SOガイドラインに準拠するようにここにカットアンドペーストします.

traitletsのドキュメント

構成システムに求めていた主な要件は次のとおりです。

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

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

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

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

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

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

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

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

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

彼らの言葉で:

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

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

アプリケーションには、設定済みのロガーであるログ属性が常にあります。これにより、アプリケーションごとに集中ロギング設定が可能になります。構成可能:構成可能

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

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

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

2
jLi