【问题标题】:Updating a property used in dict in a class更新类中 dict 中使用的属性
【发布时间】:2021-10-30 17:00:05
【问题描述】:

我正在尝试实现动态更新对象中的一些参数。 让我们有一个我在某些 dict 中使用的属性 ->

class Test():
    def __init__(self):
        self._id = None
        self.some_dict = {"id": self.id}

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, new_id: int):
        self._id = new_id

tst = Test()
tst.id = 112
print(tst.id) # 112
print(tst.some_dict) # {'id': None}

想要的结果是:

print(tst.some_dict) # {'id': 112}

如何在某些参数(id)作为属性被更新后更新字典?

编辑: 我当然可以:

@id.setter
def id(self, new_id: int):
    self._id = new_id
    self._some_dict.update({"id": self._id})

但是很多参数写起来有点累,有没有自动的方法?

【问题讨论】:

  • 字典不会隐式更新,因为其他一些变量会更新。您要么需要在 setter 中显式更新 dict,要么将 some_dict 设为 @property 并始终即时创建它。
  • "有没有自动的方法?"好吧,您可以在用户要求时自动创建字典。您已经知道要使用的工具 - 它与您现在使用的工具相同,用于在用户请求时自动创建 id 值。
  • 您的代码中没有self._some_dict。 (错字?)您使用的 getter 和 setter 没有 任何特殊功能,只需使用 self.id 代替 - 除非您真的需要一个 getter/setter 来进行 logic 。没有它,python 工作得很好——而且它更简单/更容易理解
  • 你总是可以在你的课堂上放弃self._id = None,而是从你用作“通用”支持字段的dict中提取/放置所有值(通过getter/setter)——但这并不多感觉该类已经有一个存储其所有成员的字典 ...

标签: python dictionary properties


【解决方案1】:

您必须将行 self.some_dict['id'] = new_id 添加到 def id

【讨论】:

    【解决方案2】:

    您可以访问实例的__dict__ 属性并返回此dict 的净化版本。

    示例:(应该按原样工作)

    class A:
        _prohibited_keys = ["instance", "variables", "you", "dont", "want", "to", "show"]
    
        def __init__(self):
            self._id = None
        @property
        def id(self):
            return self._id
    
        @id.setter
        def id(self, value: int):
            self._id = value
    
        @property
        def dict(self):
            return {self._private_to_pub_key(k): v for k, v in self.__dict__.items()
                    if k not in self._prohibited_keys and not k.startswith('__')}
    
        def _private_to_pub_key(self, k):
            return k[1:] if k.startswith('_') else k
    
    
    
    
    a = A()
    print(a.dict)
    >>> {'id': None}
    

    【讨论】:

    • 如果函数内部没有逻辑但题外话,则根本不需要 getter/setter - 次要的挑剔:如果你碰巧在你的类中创建了自己的 dunder 方法,你的 private_to_pub_key 将删除前 2 个下划线之一。
    • 我其实在我的代码中使用了这个方法:return sanitize(k): v for k, v in self.__dict__.items() if k not in self._prohibited_keys and not k.startswith('__')
    【解决方案3】:
    class Test():
        def __init__(self):
            self._id = None
            self.some_dict = {"id": self.id}
        
        @property
        def id(self):
            return self._id
        
        @id.setter
        def id(self, new_id: int):
            self._id = new_id
            self.some_dict['id'] = new_id
    
    
    
    tst = Test()
    tst.id = 112
    print(tst.id) # 112
    print(tst.some_dict) # {'id': 112}
    

    【讨论】:

    • 请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
    • 虽然这段代码可以回答这个问题,但最好包含一些上下文,解释它如何工作以及何时使用它。从长远来看,纯代码的答案没有用处。
    【解决方案4】:

    你可以在id.setter函数里面更新它:

        @id.setter
        def id(self, new_id: int):
            self._id = new_id
            self.some_dict.update({'id': new_id})
    

    【讨论】:

      【解决方案5】:

      可以在某种程度上实现自动化,例如消除大量重复编码。在下面的代码中,这是通过利用创建属性的实用函数以及对类定义进行后处理并初始化您也希望它也具有的字典的元类的组合来完成的。

      后者涉及搜索那些已定义并初始化与其属性名称关联的实例存储属性以及此附加字典中的相应项。这个要求使得创建类实例变得如此复杂,尤其是需要有一个元类——所以你可能需要重新考虑这是否真的有必要。

      def dict_property(name):
          """ Define property with given name that updates a private attribute and the
              instance's dictionary named some_dict.
          """
          storage_name = '_' + name
      
          @property
          def prop(self):
              return getattr(self, storage_name)
      
          @prop.setter
          def prop(self, value):
              setattr(self, storage_name, value)
              self.some_dict.update({name: value})
      
          return prop
      
      
      class MyMetaClass(type):
          def __new__(cls, name, bases, classdict, **kwargs):
              """ Initialize dict_property values in class and some_dict to None. """
      
              names = []
              for name, value in classdict.items():
                  if isinstance(value, property):
                      #print(f'found property named {name!r}')
                      names.append(name)
      
              some_dict = {}
              for name in names:
                  storage_name = '_' + name
                  classdict[storage_name] = None
                  some_dict[name] = None
      
              classdict['some_dict'] = some_dict  # Add dict to class.
      
              # Create the class.
              return type.__new__(cls, name, bases, classdict, **kwargs)
      
      
      class Test(metaclass=MyMetaClass):
          id = dict_property('id')
          age = dict_property('age')
      
      
      tst = Test()
      print(f'{tst.id=}')         # -> tst.id=None
      print(f'{tst.age=}')        # -> tst.age=None
      print(f'{tst.some_dict=}')  # -> tst.some_dict={'id': None, 'age': None}
      print()
      tst.id = 112
      print(f'{tst.id=}')         # -> tst.id=112
      print(f'{tst.age=}')        # -> tst.age=None
      print(f'{tst.some_dict=}')  # -> tst.some_dict={'id': 112, 'age': None}
      print()
      tst.age = 42
      print(f'{tst.id=}')         # -> tst.id=112
      print(f'{tst.age=}')        # -> tst.age=42
      print(f'{tst.some_dict=}')  # -> tst.some_dict={'id': 112, 'age': 42}
      
      

      【讨论】:

        【解决方案6】:

        尝试使用 @property 装饰器和 some_dict

        class Test():
            def __init__(self):
                self._id = None
        
            @property
            def id(self):
                return self._id
        
            @id.setter
            def id(self, new_id: int):
                self._id = new_id
        
            @property
            def some_dict(self):
                return {'id': self.id}
        
        tst = Test()
        tst.id = 112
        print(tst.id) # 112
        print(tst.some_dict) # {'id': 112}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-03-05
          • 1970-01-01
          • 1970-01-01
          • 2017-01-13
          • 2018-09-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多