Dataclasses and property decorator

前端 未结 10 1658
没有蜡笔的小新
没有蜡笔的小新 2020-12-15 04:43

I\'ve been reading up on Python 3.7\'s dataclass as an alternative to namedtuples (what I typically use when having to group data in a structure). I was wondering if datacla

10条回答
  •  天命终不由人
    2020-12-15 05:00

    TWO VERSIONS THAT SUPPORT DEFAULT VALUES

    Most published approaches don't provide a readable way to set a default value for the property, which is quite an important part of dataclass. Here are two possible ways to do that.

    The first way is based on the approach referenced by @JorenV. It defines the default value in _name = field() and utilises the observation that if no initial value is specified, then the setter is passed the property object itself:

    from dataclasses import dataclass, field
    
    
    @dataclass
    class Test:
        name: str
        _name: str = field(init=False, repr=False, default='baz')
    
        @property
        def name(self) -> str:
            return self._name
    
        @name.setter
        def name(self, value: str) -> None:
            if type(value) is property:
                # initial value not specified, use default
                value = Test._name
            self._name = value
    
    
    def main():
        obj = Test(name='foo')
        print(obj)                  # displays: Test(name='foo')
    
        obj = Test()
        obj.name = 'bar'
        print(obj)                  # displays: Test(name='bar')
    
        obj = Test()
        print(obj)                  # displays: Test(name='baz')
    
    
    if __name__ == '__main__':
        main()
    

    The second way is based on the same approach as @Conchylicultor: bypassing the dataclass machinery by overwriting the field outside the class definition.

    Personally I think this way is cleaner and more readable than the first because it follows the normal dataclass idiom to define the default value and requires no 'magic' in the setter.

    Even so I'd prefer everything to be self-contained... perhaps some clever person can find a way to incorporate the field update in dataclass.__post_init__() or similar?

    from dataclasses import dataclass
    
    
    @dataclass
    class Test:
        name: str = 'foo'
    
        @property
        def _name(self):
            return self._my_str_rev[::-1]
    
        @_name.setter
        def _name(self, value):
            self._my_str_rev = value[::-1]
    
    
    # --- has to be called at module level ---
    Test.name = Test._name
    
    
    def main():
    
        obj = Test()
        print(obj)                      # displays: Test(name='foo')
    
        obj = Test()
        obj.name = 'baz'
        print(obj)                      # displays: Test(name='baz')
    
        obj = Test(name='bar')
        print(obj)                      # displays: Test(name='bar')
    
    
    if __name__ == '__main__':
        main()
    

提交回复
热议问题