【问题标题】:Update parent dict in subclass更新子类中的父字典
【发布时间】:2017-07-01 18:07:15
【问题描述】:

我有这样的课程:

class Parent(object):
    _defaults = {
        'key': 'value',
        'key2': 'value',
    }

class Child(Parent):
    _defaults = {
        'key2': 'value2',
        'key3': 'value2',
    }

创建子类时,我希望将_defaults 设置为父类_defaults,然后使用子类_defaults 进行更新。所以实际解析的类应该是这样的:

class Child(object):
    _defaults = {
        'key': 'value',
        'key2': 'value2',
        'key3': 'value2',
    }

这可以通过__metaclass__ 构造函数或其他方式实现吗?

【问题讨论】:

    标签: python python-3.x class metaclass


    【解决方案1】:

    您可以使用collections.ChainMap 完成此操作

    import collections
    
    class Child(Parent):
        _defaults = collections.ChainMap({
            'key2': 'value2',
            'key3': 'value2',
        }, Parent._defaults)
    

    当然,您可以编写一个元类来为您组合这些东西,但要弄清楚(通常)哪些地图应该与超类上的映射以及应该覆盖的映射...

    【讨论】:

      【解决方案2】:

      直接读取字典项,然后更新?

      class Parent(object):
          _defaults = {
              'key': 'value',
              'key2': 'value',
          }
      
      
      class Child(Parent):
          _defaults = {k: v for k, v in Parent._defaults.items()}
          _defaults.update({'key2': 'value2',
                            'key3': 'value2'})
      
      
      testpar = Parent()
      test = Child()
      
      print(testpar._defaults)
      print(test._defaults)
      

      打印:

      {'key2': 'value', 'key': 'value'}
      {'key3': 'value2', 'key2': 'value2', 'key': 'value'}
      

      在 2.7 和 3.5 中

      【讨论】:

      • 为什么不_defaults = Parent._defaults.copy()
      【解决方案3】:

      使用元类,您需要在__new__ 方法中执行此操作,以便在实例化之前应用更改。由于__new__ 方法接受cls, name, bases, namespace, **kwds 作为参数,您可以使用bases 参数(它是所有基类的元组)简单地访问父对象,然后您可以简单地更新您的预期属性。

      class MyMetaClass(type):
      
          def __new__(cls, name, bases, namespace, **kwds):
              bases[0]._defaults.update(namespace['_defaults'])
              result = type.__new__(cls, name, bases, dict(namespace))
              return result
      
      
      class Parent(object):
          _defaults = {
              'key': 'value',
              'key2': 'value',
          }
      
      class Child(Parent, metaclass=MyMetaClass):
          _defaults = {
              'key2': 'value2',
              'key3': 'value2',
          }
      

      演示:

      c = Child()    
      print(type(c).__bases__[0]._defaults)
      {'key2': 'value2', 'key3': 'value2', 'key': 'value'}
      

      请注意,正如@jsbueno 提到的,此方法有一个大问题,即在每次实例化时更新父类的_defaults 属性。拒绝这个问题的一种方法可能是为元类提供一个标志,以便只更新一次父类。

      【讨论】:

      • 理想情况下你会使用ChainMap,正如另一个答案中给出的那样,但这已经足够好了。
      • @xaav 是的,这是一个好方法。但是我发布了这个答案,因为我认为 OP 正在为教育目的寻找基于元类的解决方案。
      • @jsbueno 我很高兴将不同的答案标记为正确,它可以更好地解决问题。事实上,我对这个答案并不完全满意,但它是唯一一个在元类中加入默认值的答案,这正是我正在寻找的。​​span>
      • @jsbueno 确实,这是一个很好的观点。是的,我应该提到那个机器人,不幸的是我错过了它。我会更新问题。感谢关注。
      • @xaav,这主要是好的 - 我认为它可以让人们更喜欢成为一个很好的参考,有时只是在需要时“复制和粘贴”代码。这种情况并不少见,有时需要元类解决方案而不是链图。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-08
      • 1970-01-01
      • 2019-05-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多