config.py에서 전역 구성 변수를 제공하는 대부분의 Pythonic 방법은 무엇입니까?
단순한 것들을 지나치게 복잡하게 만드는 나의 끝없는 탐구에서, 나는 파이썬 달걀 패키지에서 발견되는 전형적인 ' 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))
말이 되긴하지만, 특히 특정 변수의 이름을 기억하려고 할 때 약간 지저분해질 수 있습니다. 게다가 변수를 속성으로 사용 하는 'configuration'객체를 제공하는 것이 더 유연 할 수 있습니다. 그래서 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))
패키지 내에서 전역 변수를 저장하고 가져 오는 더 읽기 쉽고 표현력 있고 유연한 방법으로 보입니다.
가장 빈약 한 아이디어? 이러한 상황에 대처하기위한 모범 사례는 무엇입니까? 무엇 당신의 저장 및 글로벌 이름과 변수 패키지 내부를 가져 오는 방식은?
한 번 했어요. 궁극적으로 단순화 된 basicconfig.py 가 내 필요에 적합 하다는 것을 알았습니다 . 필요한 경우 참조 할 수 있도록 다른 개체와 함께 네임 스페이스를 전달할 수 있습니다. 코드에서 추가 기본값을 전달할 수도 있습니다. 또한 속성 및 매핑 스타일 구문을 동일한 구성 개체에 매핑합니다.
다음과 같은 내장 유형을 사용하는 것은 어떻습니까?
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 과 같은 라이브러리를 사용 하여 구성 파일을 편리 하게 구문 분석하고 액세스합니다.
이 솔루션 은 소규모 응용 프로그램에 적합합니다 .
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의 답변 ). 소규모 애플리케이션의 경우 위의 솔루션이 더 간단합니다.
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
수업은 어떻습니까?
# config.py
class MYSQL:
PORT = 3306
DATABASE = 'mydb'
DATABASE_TABLES = ['tb_users', 'tb_groups']
# main.py
from config import MYSQL
print(MYSQL.PORT) # 3306
내가 사용하는 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)
People forget classes exist, even if no object is ever instantiated that is a member of that class. And variables in a class that aren't preceded by 'self.' are shared across all instances of the class, even if there are none. Once 'debug' is changed by any code, all other code sees the change.
By importing it as gl, you can have multiple such files and variables that lets you access and set values across code files, functions, etc., but with no danger of namespace collision.
This lacks some of the clever error checking of other approaches, but is simple and easy to follow.
please check out the IPython configuration system, implemented via traitlets for the type enforcement you are doing manually.
Cut and pasted here to comply with SO guidelines for not just dropping links as the content of links changes over time.
Here are the main requirements we wanted our configuration system to have:
Support for hierarchical configuration information.
Full integration with command line option parsers. Often, you want to read a configuration file, but then override some of the values with command line options. Our configuration system automates this process and allows each command line option to be linked to a particular attribute in the configuration hierarchy that it will override.
Configuration files that are themselves valid Python code. This accomplishes many things. First, it becomes possible to put logic in your configuration files that sets attributes based on your operating system, network setup, Python version, etc. Second, Python has a super simple syntax for accessing hierarchical data structures, namely regular attribute access (Foo.Bar.Bam.name). Third, using Python makes it easy for users to import configuration attributes from one configuration file to another. Fourth, even though Python is dynamically typed, it does have types that can be checked at runtime. Thus, a 1 in a config file is the integer ‘1’, while a '1' is a string.
A fully automated method for getting the configuration information to the classes that need it at runtime. Writing code that walks a configuration hierarchy to extract a particular attribute is painful. When you have complex configuration information with hundreds of attributes, this makes you want to cry.
Type checking and validation that doesn’t require the entire configuration hierarchy to be specified statically before runtime. Python is a very dynamic language and you don’t always know everything that needs to be configured when a program starts.
To acheive this they basically define 3 object classes and their relations to each other:
1) Configuration - basically a ChainMap / basic dict with some enhancements for merging.
2) Configurable - base class to subclass all things you'd wish to configure.
3) Application - object that is instantiated to perform a specific application function, or your main application for single purpose software.
In their words:
Application: Application
An application is a process that does a specific job. The most obvious application is the ipython command line program. Each application reads one or more configuration files and a single set of command line options and then produces a master configuration object for the application. This configuration object is then passed to the configurable objects that the application creates. These configurable objects implement the actual logic of the application and know how to configure themselves given the configuration object.
Applications always have a log attribute that is a configured Logger. This allows centralized logging configuration per-application. Configurable: Configurable
A configurable is a regular Python class that serves as a base class for all main classes in an application. The Configurable base class is lightweight and only does one things.
This Configurable is a subclass of HasTraits that knows how to configure itself. Class level traits with the metadata config=True become values that can be configured from the command line and configuration files.
Developers create Configurable subclasses that implement all of the logic in the application. Each of these subclasses has its own configuration information that controls how instances are created.
'Nice programing' 카테고리의 다른 글
xUnit에서 컬렉션 크기를 확인하는 관용적 인 방법은 무엇입니까? (0) | 2020.10.10 |
---|---|
ReadOnlyObservableCollection.CollectionChanged가 공개되지 않는 이유는 무엇입니까? (0) | 2020.10.10 |
Windows 레지스트리에서 값을 읽는 방법 (0) | 2020.10.10 |
AWT가없는 Java GUI 리스너 (0) | 2020.10.10 |
Windows 용 PHP 스레드 안전 및 비 스레드 안전 (0) | 2020.10.10 |