Как обновлять конфигурацию при каждом чтении? - PullRequest
2 голосов
/ 07 мая 2020

Итак, у меня есть этот класс:

import yaml

class Config():
        def __init__(self, filename):
                self.config_filename=filename

        def __read_config_file(self):
                with open(self.config_filename) as f:
                        self.cfg = yaml.safe_load(f)

        def get(self):
                self.__read_config_file()
                return self.cfg

И он отлично работает. Идея заключается в том, чтобы принудительно перечитывать файл конфигурации каждый раз, когда я что-то использую в конфигурации. Вот пример использования:

cfg = Config('myconfig.yaml')

for name in cfg.get()['persons']:
    print (cfg.get()['persons'][name]['phone']) 
    print (cfg.get()['persons'][name]['address']) 

Это работает, но я думаю, что это выглядит крайне некрасиво. Я мог бы сделать что-то вроде этого:

c = cfg.get()['persons']
for name in c:
    print (c['persons'][name]['phone']) 
    print (c['persons'][name]['address']) 

Это выглядит немного лучше, но я также теряю возможность перезагрузки при доступе, но я хочу сделать что-то это (что, очевидно, не работает ):

for name in c:
    print (name['phone']) 
    print (name['address'])

Похоже, что я чего-то не понимаю в итерациях по словарям, но меня больше всего беспокоит то, что я хочу перезагружать файл конфигурации каждый раз, когда используется какое-либо значение из этого файла, и я хочу, чтобы это было хорошо читаемым. Итак, как я могу это изменить?

Пример файла конфигурации. При необходимости здесь можно изменить формат.

persons:
    john:
        address: "street A"
        phone: "123"
    george:
        address: "street B"
        phone: "456"

Ответы [ 3 ]

0 голосов
/ 07 мая 2020

Я сделал ваши данные «лиц»:

persons={}
persons["name"]={"john":{'phone':'123', 'address':'street A'}, 
                 "george":{'phone':'456', 'address':'street B'}}

Вот кое-что интересное, вы можете получить все имена, написанные в «лицах»:

L_names = list(persons['name'].keys())
print(L_names)  # returns ['john', 'george']

Итак если вы получите данные каждого символа:

L_names_data = []
for i in list(persons['name'].keys()):
    L_names_data.append(persons['name'][i])

Вы можете легко написать то, что хотели: (красиво и просто для l oop)

for name_data in L_names_data:
    print(name_data['address'])
    print(name_data['phone'])

#returns :
#street A
#123
#street B
#456

Неудобство в том, что вы теряете информацию 'name' (строки 'john' и 'george' не появляются в 'L_names_data', который является списком словарей).

0 голосов
/ 08 мая 2020

Пользователь rasjani сделал несколько комментариев, которые помогли мне решить эту проблему. В основном я делаю следующее:

import collections

class Config(collections.UserDict):
        def __getitem__(self, key):
                self.reload()
                return super().__getitem__(key)

        def reload(self):
                with open(self.filename) as f:
                        c=yaml.safe_load(f)
                super().__init__(c)

Фактический класс более сложный, с большим количеством проверок ошибок и функций, но вышесказанное - это то, что делает magi c, о котором я спросил.

Метод __getitem__ вызывается каждый раз, когда вы используете словарь. Поэтому я просто вызываю reload перед вызовом __getitem__ из суперкласса.

Здесь следует знать об одном исключении.

a = Config('myconf.yml')
b = a['field'] # Will reload the configuration file
c = a          # But this will not
d = c['field'] # However, this will

Я думал о решении этого вопроса также модифицируя __getattribute__ таким же образом, но у меня возникли всевозможные проблемы с бесконечной рекурсией, и на практике я не уверен, стоит ли это решать или вообще есть ли проблема.

0 голосов
/ 07 мая 2020

Так как это dict, обычная итерация не работает. мы можем перебирать словарь следующим способом:

for name, person in c.items():
   print(name)
   print(person['phone']) 
   print(person['address'])

Надеюсь, это поможет

...