Większość Pythonicznych sposobów dostarczania globalnych zmiennych konfiguracyjnych w config.py?

W moim niekończącym się poszukiwaniu skomplikowanych prostych rzeczy, badam najbardziej "Pythoniczny" sposób dostarczenia globalnych zmiennych konfiguracyjnych wewnątrz typowego " config.py ' Znalezione w pakietach Python egg.

Tradycyjny sposób (aah, good ol ' # define !) jest następująca:

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

Dlatego zmienne globalne są importowane w jeden z następujących sposobów:

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

Lub:

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

To ma sens, ale czasami może być trochę bałagan, zwłaszcza gdy próbujesz zapamiętać nazwy pewnych zmiennych. Poza tym, zapewnienie ' configuration ' obiektu , ze zmiennymi jako atrybutami , może być bardziej elastyczne. / Align = "center" bgcolor = "# e0ffe0 " / cesarz Chin / / align = center / config.py plik, wymyśliłem:

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

I "config.py" importuje klasę i brzmi następująco:

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'

I jest używany w ten sposób:

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

Który wydaje się bardziej czytelny, ekspresyjny i elastyczny sposób przechowywania i pobierania zmiennych globalnych wewnątrz paczka.

Lamest idea ever? Jaka jest najlepsza praktyka radzenia sobie z takimi sytuacjami? Jaki jest Twój sposób przechowywania i pobierania globalnych nazw i zmiennych wewnątrz Twojego pakietu?
Author: Rigel Di Scala, 2011-06-01

7 answers

 5
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-12 20:35:48

Może po prostu użyj wbudowanych typów, takich jak ten:

config = {
    "mysql": {
        "user": "root",
        "pass": "secret",
        "tables": {
            "users": "tb_users"
        }
        # etc
    }
}

Możesz uzyskać dostęp do wartości w następujący sposób:

config["mysql"]["tables"]["users"]

Jeśli chcesz poświęcić potencjał do obliczania wyrażeń wewnątrz drzewa konfiguracyjnego, możesz użyć YAML i skończyć z bardziej czytelnym plikiem konfiguracyjnym, takim jak:

mysql:
  - user: root
  - pass: secret
  - tables:
    - users: tb_users

I użyj biblioteki podobnej do PyYAML , aby conventiently parsować i uzyskać dostęp do pliku konfiguracyjnego

 47
Author: blubb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 20:41:54

Podobne do odpowiedzi blubba. Proponuję zbudować je za pomocą funkcji lambda, aby zredukować kod. Tak:

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
To śmierdzi, jakbyś chciał zrobić klasę.

Lub, jak zauważył MarkM, możesz użyć 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
 8
Author: Cory B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 17:37:15

Podoba mi się to rozwiązanie do małych aplikacji :

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

I wtedy użycie to:

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

.. powinno ci się spodobać, ponieważ:

  • używa zmiennych klasy (nie jest wymagany żaden obiekt/ singleton),
  • używa wbudowanych typów i wygląda jak (jest) wywołanie metody na App,
  • ma kontrolę nad indywidualną konfiguracją niezmienność, mutable globals are the worst kind of globals .
  • Promuje konwencjonalny i dobrze nazwany dostęp / czytelność w kodzie źródłowym
  • jest prostą klasą , ale wymusza dostęp strukturalny , alternatywą jest użycie @property, ale wymaga to większej liczby zmiennych obsługujących kod na element i jest oparte na obiektach.
  • wymaga minimalnych zmian , aby dodać nowe elementy konfiguracji i ustawić jego zmienność.

--Edit -- : Dla dużych aplikacji, Zapisywanie wartości w YAML (tj. właściwości) plik i odczyt, że w jako niezmiennych danych jest lepszym podejściem (tj. blubb/ohaal ' s answer ). W przypadku małych aplikacji powyższe rozwiązanie jest prostsze.

 5
Author: pds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-06 07:46:23

Jak o użyciu klas?

# 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
Author: Husky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 22:41:51

Mała wariacja na temat pomysłu Husky ' ego, którego używam. Utwórz plik o nazwie 'globals' (lub cokolwiek chcesz), a następnie zdefiniuj wiele klas w nim, jako takie:

#globals.py

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

class runtime :
    debug = False
    output = 'stdio'

Wtedy, jeśli masz dwa pliki kodu c1.py oraz c2.py, oba mogą mieć na górze

import globals as gl

Teraz cały kod może mieć dostęp i ustawiać wartości, jako takie:

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

Ludzie zapominają, że klasy istnieją, nawet jeśli nie powstał żaden obiekt, który jest członkiem tej klasy. Oraz zmienne w klasie, które nie są poprzedzone przez ' self."are współdzielone przez wszystkie instancje klasy, nawet jeśli ich nie ma. Gdy' debug ' zostanie zmieniony przez dowolny kod, wszystkie inne kody widzą tę zmianę.

Importując go jako gl, możesz mieć wiele takich plików i zmiennych, które umożliwiają dostęp i ustawianie wartości w plikach kodu, funkcjach itp., ale bez niebezpieczeństwa kolizji przestrzeni nazw.

Brakuje tu sprytnego sprawdzania błędów innych podejść, ale jest to proste i łatwe do naśladowania.

 2
Author: eSurfsnake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-13 00:57:15

Zapoznaj się z systemem konfiguracji IPython, zaimplementowanym za pomocą traitletów dla wymuszania typów, które wykonujesz ręcznie.

Wytnij i wklej tutaj, aby zachować zgodność z wytycznymi dotyczącymi nie tylko porzucania linków, ponieważ zawartość linków zmienia się w czasie.

Traitlets documentation

Oto główne wymagania, które chcieliśmy, aby nasz system konfiguracyjny miał:

Wsparcie dla hierarchicznych informacji konfiguracyjnych.

Full integracja z parserami opcji wiersza poleceń. Często chcesz przeczytać plik konfiguracyjny, ale następnie nadpisać niektóre wartości opcjami wiersza poleceń. Nasz system konfiguracji automatyzuje ten proces i pozwala każdej opcji wiersza poleceń, aby być połączone z określonym atrybutem w hierarchii konfiguracji, który będzie nadpisany.

Pliki konfiguracyjne, które same są poprawnym kodem Pythona. To osiąga wiele rzeczy. Po pierwsze, staje się możliwe, aby umieścić logikę w swoim pliki konfiguracyjne, które ustawiają atrybuty na podstawie systemu operacyjnego, konfiguracji sieci, wersji Pythona itp. Po drugie, Python ma bardzo prostą składnię umożliwiającą dostęp do hierarchicznych struktur danych, a mianowicie regularny dostęp do atrybutów (Foo.Bar.Bam.name). po trzecie, Korzystanie z Pythona ułatwia użytkownikom importowanie atrybutów konfiguracji z jednego pliku konfiguracyjnego do drugiego. Po czwarte, mimo że Python jest pisany dynamicznie, ma typy, które można sprawdzać w czasie wykonywania. Tak więc, 1 w pliku konfiguracyjnym jest liczba całkowita "1", podczas gdy " 1 " jest ciągiem znaków.

W pełni zautomatyzowana metoda pobierania informacji o konfiguracji do klas, które jej potrzebują w czasie wykonywania. Pisanie kodu, który przechodzi przez hierarchię konfiguracji, aby wyodrębnić konkretny atrybut, jest bolesne. Gdy masz złożone informacje konfiguracyjne z setkami atrybutów, to sprawia, że chcesz płakać.

Sprawdzanie i Walidacja typu, które nie wymaga statycznie określenia całej hierarchii konfiguracji przed runtime. Python jest bardzo dynamicznym językiem i nie zawsze wiesz wszystko, co trzeba skonfigurować, gdy program się uruchamia.

Aby to osiągnąć, zasadniczo definiują 3 klasy obiektów i ich relacje ze sobą:

1) Konfiguracja-zasadniczo ChainMap / basic dict z pewnymi ulepszeniami do scalania.

2) konfigurowalny - klasa bazowa do podklasowania wszystkich rzeczy, które chcesz skonfigurować.

3) Aplikacja-obiekt, który jest instancją do wykonania konkretna funkcja aplikacji lub główna aplikacja dla oprogramowania do jednego celu.

W ich słowach:

Application: Application

Aplikacja jest procesem, który wykonuje określone zadanie. Najbardziej oczywistą aplikacją jest program wiersza poleceń ipython. Każda aplikacja odczytuje jeden lub więcej plików konfiguracyjnych i pojedynczy zestaw opcji wiersza poleceń, a następnie tworzy główny obiekt konfiguracyjny dla aplikacji. Ten obiekt konfiguracyjny jest następnie przekazywany do konfigurowalnych obiektów, które aplikacja wytworzy. Te konfigurowalne obiekty implementują rzeczywistą logikę aplikacji i wiedzą, jak skonfigurować się w danym obiekcie konfiguracyjnym.

Aplikacje zawsze mają atrybut log, który jest skonfigurowanym Loggerem. Umożliwia to scentralizowaną konfigurację logowania dla poszczególnych aplikacji. Konfigurowalny: Konfigurowalny

Konfigurowalny jest zwykłą klasą Pythona, która służy jako klasa podstawowa dla wszystkich klas Głównych w podanie. Konfigurowalna klasa podstawowa jest lekka i robi tylko jedną rzecz.

Ten konfigurowalny jest podklasą HasTraits, która wie, jak skonfigurować się. Cechy poziomu klasy z metadanymi config = True stają się wartościami, które można skonfigurować z linii poleceń i plików konfiguracyjnych.

Programiści tworzą konfigurowalne podklasy, które implementują całą logikę w aplikacji. Każda z tych podklas ma swoje własne informacje konfiguracyjne, które sterują sposób tworzenia instancji.

 0
Author: jLi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-14 15:55:11