config.py에서 전역 구성 변수를 제공하는 대부분의 Pythonic 방법은 무엇입니까? [닫은]


99

단순한 일을 지나치게 복잡하게 만드는 끝없는 탐구에서 저는 파이썬 달걀 패키지에 있는 전형적인 ' config.py ' 내부에 전역 구성 변수를 제공하는 가장 ' 파이 토닉'방법을 연구하고 있습니다.

전통적인 방법 (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))

이치에 맞지만, 특히 특정 변수의 이름을 기억하려고 할 때 약간 지저분해질 수 있습니다. 게다가하는 제공하는 '구성'개체 와, 속성으로 변수를 보다 유연 수 있습니다. 따라서 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에 대한 다른 문법과 같은 다른 방식으로 저장할 수 있습니다. 구성 파일 (도구, 사람, 배경은 무엇입니까?)을 작성하는 사람에 따라 다른 문법이 선호 될 수 있습니다. 대부분의 경우 구성 파일을 프로그램에 사용하는 언어와 동일한 언어로 작성하는 것은 좋지 않을 수 있습니다. 사용자에게 너무 많은 권한을 부여하기 때문입니다 (자신 일 수도 있지만 자신이 할 수있는 모든 것을 기억하지 못할 수도 있음). 몇 달 전에 잘못됨).
erikbwork

4
종종 JSON 구성 파일을 작성합니다. 파이썬 구조로 쉽게 읽을 수 있으며 도구로도 만들 수 있습니다. 유연성이 가장 높은 것으로 보이며 유일한 비용은 사용자에게 귀찮을 수있는 일부 교정기입니다. 그래도 나는 달걀을 쓴 적이 없다. 아마도 그것이 표준적인 방법 일 것입니다. 이 경우 위의 내 의견을 무시하십시오.
erikbwork

1
당신은 대신 "(자기) 바르"를 사용할 수 있습니다 "자기 .__ DICT __ 키 ()."
Karlisson

1
Python에서 설정 파일을 사용하는 가장 좋은 방법무엇입니까? 그들은 "많은 방법이 가능하고 자전거 통행 스레드가 이미 존재합니다. 보안에 관심이 없다면 config.py가 좋습니다."라고 대답합니다.
Nikana Reklawyks 2011

나는을 사용하여 결국 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 모듈 의 일부입니다 .
Keith

57

다음과 같은 내장 유형을 사용하는 것은 어떻습니까?

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
뭔가의 존재는 ^^ 메모리에 데이터를 유지한다면
cinatic

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것이지만 항목 당 더 많은 변수 처리 코드가 필요하며 객체 기반입니다.
  • 새 구성 항목을 추가하고 변경 가능성을 설정 하려면 최소한의 변경필요합니다 .

--Edit-- : 대규모 애플리케이션의 경우 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

오 예 ...이 멍청한 예를 모았습니다. 나는 이름이 변경됩니다
코리-G

이러한 접근 방식에서는 mkDict람다 대신 클래스를 고려할 수 있습니다 . class를 호출하면 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

7

내가 사용하는 Husky의 아이디어에 대한 작은 변형. 'globals'(또는 원하는대로)라는 파일을 만든 다음 다음과 같이 여러 클래스를 정의합니다.

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

사람들은 해당 클래스의 구성원 인 개체가 인스턴스화되지 않은 경우에도 클래스가 존재하는 것을 잊습니다. 그리고 'self'가 앞에 나오지 않는 클래스의 변수. 클래스의 모든 인스턴스에서 공유됩니다. '디버그'가 코드에 의해 변경되면 다른 모든 코드가 변경 사항을 확인합니다.

gl으로 가져 오면 이러한 파일과 변수를 여러 개 가질 수있어 코드 파일, 함수 등에서 값을 액세스하고 설정할 수 있지만 네임 스페이스 충돌의 위험은 없습니다.

이것은 다른 접근법의 영리한 오류 검사가 부족하지만 간단하고 따르기 쉽습니다.


1
globals현재 전역 범위의 모든 기호와 함께 dict를 반환하는 내장 함수이기 때문에 module 이름을 지정하는 것은 잘못된 것 입니다. 또한 PEP8은 클래스 (예 :)에는 CamelCase (모두 대문자 포함 DBInfo)를, 소위 상수 (예 :)에는 밑줄이있는 대문자를 권장 DEBUG합니다.
Nuno André

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를 사용하는 것이 좋지만 값이 변경되지 않고 그냥 호출하여 사용하면 of.ini 파일 사용에 동의합니다.
Kourosh

3

수동으로 수행하는 유형 적용에 대한 트레 이틀 릿을 통해 구현 된 IPython 구성 시스템을 확인하십시오.

시간이 지남에 따라 링크 내용이 변경됨에 따라 링크를 삭제하는 것이 아니라 SO 지침을 준수하기 위해 여기에 잘라내어 붙여 넣었습니다.

트레 이틀 릿 문서

다음은 구성 시스템에 필요한 주요 요구 사항입니다.

계층 적 구성 정보 지원.

명령 줄 옵션 파서와 완벽하게 통합됩니다. 종종 구성 파일을 읽으려고하지만 명령 줄 옵션으로 일부 값을 재정의합니다. 우리의 구성 시스템은이 프로세스를 자동화하고 각 명령 줄 옵션을 재정의 할 구성 계층의 특정 속성에 연결할 수 있도록합니다.

그 자체로 유효한 Python 코드 인 구성 파일입니다. 이것은 많은 것을 성취합니다. 첫째, 운영 체제, 네트워크 설정, Python 버전 등에 따라 속성을 설정하는 구성 파일에 로직을 넣을 수 있습니다. 둘째, Python은 계층 적 데이터 구조에 액세스하기위한 매우 간단한 구문, 즉 정규 속성 액세스 (Foo. Bar.Bam.name). 셋째, Python을 사용하면 사용자가 한 구성 파일에서 다른 구성 파일로 구성 속성을 쉽게 가져올 수 있습니다. 넷째, Python은 동적으로 입력되지만 런타임에 확인할 수있는 유형이 있습니다. 따라서 구성 파일의 1은 정수 '1'이고 '1'은 문자열입니다.

구성 정보를 런타임에 필요한 클래스로 가져 오는 완전히 자동화 된 방법입니다. 특정 속성을 추출하기 위해 구성 계층 구조를 걷는 코드를 작성하는 것은 어렵습니다. 수백 개의 속성이 포함 된 복잡한 구성 정보가 있으면 울고 싶어 질 것입니다.

런타임 전에 전체 구성 계층을 정적으로 지정할 필요가없는 유형 검사 및 유효성 검사. Python은 매우 동적 인 언어이며 프로그램이 시작될 때 구성해야하는 모든 것을 항상 알 수는 없습니다.

이를 달성하기 위해 기본적으로 3 개의 객체 클래스와 서로에 대한 관계를 정의합니다.

1) 구성-기본적으로 병합을위한 일부 개선 사항이있는 ChainMap / 기본 사전.

2) 구성 가능-구성하려는 모든 것을 하위 클래스로 만드는 기본 클래스입니다.

3) 응용 프로그램-특정 응용 프로그램 기능을 수행하기 위해 인스턴스화되는 개체 또는 단일 목적 소프트웨어의 기본 응용 프로그램입니다.

그들의 말로 :

신청 : 신청

응용 프로그램은 특정 작업을 수행하는 프로세스입니다. 가장 확실한 응용 프로그램은 ipython 명령 줄 프로그램입니다. 각 응용 프로그램은 하나 이상의 구성 파일과 단일 명령 줄 옵션 집합을 읽은 다음 응용 프로그램에 대한 마스터 구성 개체를 생성합니다. 이 구성 개체는 응용 프로그램이 만드는 구성 가능한 개체에 전달됩니다. 이러한 구성 가능한 개체는 응용 프로그램의 실제 논리를 구현하고 주어진 구성 개체를 구성하는 방법을 알고 있습니다.

응용 프로그램에는 항상 구성된 로거 인 로그 속성이 있습니다. 이를 통해 애플리케이션 별 중앙 집중식 로깅 구성이 가능합니다. 구성 가능 : 구성 가능

구성 가능 항목은 애플리케이션의 모든 기본 클래스에 대한 기본 클래스로 사용되는 일반 Python 클래스입니다. Configurable 기본 클래스는 가볍고 한 가지 작업 만 수행합니다.

이 Configurable은 자체 구성 방법을 알고있는 HasTraits의 하위 클래스입니다. 메타 데이터가 config = True 인 클래스 수준 특성은 명령 줄 및 구성 파일에서 구성 할 수있는 값이됩니다.

개발자는 응용 프로그램의 모든 논리를 구현하는 구성 가능한 하위 클래스를 만듭니다. 이러한 각 하위 클래스에는 인스턴스 생성 방법을 제어하는 ​​자체 구성 정보가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.