【问题标题】:Force type conversion in python dataclass __init__ method在 python 数据类 __init__ 方法中强制类型转换
【发布时间】:2019-07-18 16:40:44
【问题描述】:

我有以下非常简单的数据类:

import dataclasses

@dataclasses.dataclass
class Test:
    value: int

我创建了一个类的实例,但我使用字符串而不是整数:

>>> test = Test('1')
>>> type(test.value)
<class 'str'>

我真正想要的是强制转换为我在类定义中定义的数据类型:

>>> test = Test('1')
>>> type(test.value)
<class 'int'>

我必须手动编写__init__ 方法还是有简单的方法来实现这一点?

【问题讨论】:

    标签: python python-dataclasses


    【解决方案1】:

    在强制或检查类型的意义上,从不遵守数据类属性的类型提示。大多数像 mypy 这样的静态类型检查器都应该完成这项工作,而 Python 不会在运行时这样做,因为它从来没有这样做过。

    如果要添加手动类型检查代码,请在 __post_init__ 方法中进行:

    @dataclasses.dataclass
    class Test:
        value: int
    
        def __post_init__(self):
            if not isinstance(self.value, int):
                raise ValueError('value not an int')
                # or self.value = int(self.value)
    

    您可以使用dataclasses.fields(self) 来获取Field 对象的元组,这些对象指定字段和类型并循环遍历该对象以自动为每个字段执行此操作,而无需单独为每个字段编写。

    def __post_init__(self):
        for field in dataclasses.fields(self):
            value = getattr(self, field.name)
            if not isinstance(value, field.type):
                raise ValueError(f'Expected {field.name} to be {field.type}, '
                                 f'got {repr(value)}')
                # or setattr(self, field.name, field.type(value))
    

    【讨论】:

    • 谢谢!这不完全是我想要的(例外而不是转换),但感谢您的建议,我能够找到解决方案
    • 我宁愿选择异常而不是隐式转换,但我确实在 cmets 中为您提供了转换替代方案……
    • 哎呀,没看到最后一条评论!
    • 另一种 solution 使用装饰器代替 __post_init__
    【解决方案2】:

    您可以使用__post_init__ 方法实现此目的:

    import dataclasses
    
    @dataclasses.dataclass
    class Test:
        value : int
    
        def __post_init__(self):
            self.value = int(self.value)
    

    这个方法在__init__方法之后调用

    https://docs.python.org/3/library/dataclasses.html#post-init-processing

    【讨论】:

      【解决方案3】:

      使用pydantic.validate_arguments很容易实现

      只需在您的数据类中使用 validate_arguments 装饰器:

      from dataclasses import dataclass
      from pydantic import validate_arguments
      
      
      @validate_arguments
      @dataclass
      class Test:
          value: int
      
      

      然后试试你的演示,'str type' 1 将从str 转换为int

      >>> test = Test('1')
      >>> type(test.value)
      <class 'int'>
      

      如果你传递了真正错误的类型,它会引发异常

      >>> test = Test('apple')
      Traceback (most recent call last):
      ...
      pydantic.error_wrappers.ValidationError: 1 validation error for Test
      value
        value is not a valid integer (type=type_error.integer)
      

      【讨论】:

      • 它在 2021 年 4 月处于测试阶段,但看起来很棒!
      【解决方案4】:

      是的,简单的答案是自己在自己的__init__() 中进行转换。我这样做是因为我想要我的对象frozen=True

      对于类型验证,Pydandic 声称可以这样做,但我还没有尝试过:https://pydantic-docs.helpmanual.io/

      【讨论】:

        猜你喜欢
        • 2023-04-01
        • 2011-01-04
        • 2021-01-24
        • 2021-10-11
        • 2012-06-20
        • 2016-11-25
        • 1970-01-01
        • 1970-01-01
        • 2018-11-02
        相关资源
        最近更新 更多