Search code examples
pythonpython-dataclasses

How to make a python dataclass field not allow None values


After learning a little about dataclasses, it seems that a dataclass will always HAVE all the properties you defined. Adding the Optional only means that you don't have to pass a param into the constructor. I want to make a field required in the sense that it cannot be None.

So if I have

@dataclass
class SomeClass():
  a: int
  b: Optional[int] = None

test = SomeClass(a=None)

I want this to throw an error, but don't know the best way to achieve that result.

Two ideas I have are using the __post_init__ to run validation logic or maybe using pydantic.

Is there a generally accepted solution for this problem?


Solution

  • Both ways you mentioned are quite suitable, but pydantic.dataclasses will do automatic validation for you:

    from typing import Optional
    from pydantic.dataclasses import dataclass as pdataclass
    
    @pdataclass
    class SomeClass():
      a: int
      b: Optional[int] = None
    
    test = SomeClass(a=None) 
    

    Gives a verbose output/error:

        test = SomeClass(a=None)
               ^^^^^^^^^^^^^^^^^
      File "/data/.virtualenvs/test/lib/python3.11/site-packages/pydantic/_internal/_dataclasses.py", line 132, in __init__
        s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)
    pydantic_core._pydantic_core.ValidationError: 1 validation error for SomeClass
    a
      Input should be a valid integer [type=int_type, input_value=None, input_type=NoneType]
    

    With standard dataclasses you'd need to provide your own validation logic within __post_init__ method:

    from dataclasses import dataclass
    from typing import Optional
    
    @dataclass
    class SomeClass():
      a: int
      b: Optional[int] = None
    
      def __post_init__(self):
          if not isinstance(self.a, int):
              raise ValueError('Field `a` must be of integer type')
    
    test = SomeClass(a=None) 
    

        test = SomeClass(a=None)
               ^^^^^^^^^^^^^^^^^
      File "<string>", line 5, in __init__
    ..., in __post_init__
        raise ValueError('Field `a` must be of integer type')
    ValueError: Field `a` must be of integer type