【问题标题】:How to update configuration on each read?如何在每次读取时更新配置?
【发布时间】:2024-01-17 21:33:02
【问题描述】:

所以我有这门课:

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"

【问题讨论】:

  • YAML 文件是什么样的?
  • @RoadRunner 这真的重要吗?但是好的,我会更新问题。
  • 看看这里,这可能会有所帮助:*.com/questions/35968189/…
  • @klutt 好吧,最好包含一个示例文件。一些未来的读者可能不太习惯使用 YAML 来理解您的代码在做什么。
  • 我会让 Config 类成为 UserDict 的子类,然后覆盖 __getitem__() 以在返回值之前进行读取 ..

标签: python python-3.x yaml pyyaml


【解决方案1】:

因为它是一个字典,所以正常的迭代是行不通的。我们可以通过以下方法遍历字典,

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

希望对你有帮助

【讨论】:

  • 有点帮助,但这不会为每个打印语句重新加载配置文件,对吧?
  • 是的,它不会。由于您创建了一个对象cfg,它将遍历对象中的值。
【解决方案2】:

我已经制作了您的“人员”数据:

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

这里有一些有趣的东西,你可以得到所有用“persons”写的名字:

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

你可以轻松写出你想要的:(一个漂亮而简单的 for 循环)

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

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

不方便的是您丢失了“姓名”信息(“约翰”和“乔治”字符串不会出现在“L_names_data”这是一个字典列表中)。

【讨论】:

    【解决方案3】:

    用户 rasjani 制作了一些帮助我解决问题的 cmets。我所做的基本上是这样的:

    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)
    

    实际的类更复杂,有更多的错误检查和功能,但上面是我问的魔法。

    每次使用字典时都会调用__getitem__ 方法。所以我所做的只是在从超类调用__getitem__ 之前调用reload

    这里应该注意一个例外。

    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__来解决这个问题,但我最终遇到了各种各样的问题,无限递归,在实践中我不确定这是否是一个值得解决的问题,甚至是一个完全有问题。

    【讨论】: