pythonpython-3.xpython-dataclasses

Data class with argument optional only in init


I have the following simple class in Python:

class Point:
    def __init__(x: int, y: int | None = None):
        self.x = x
        self.y = y if y is not None else x

How can the same thing be implemented using a @dataclass? The obvious would be to do the following:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int | None

    def __post_init__(self):
        if self.y is None:
            self.y = self.x

This appears to work, but changes type of y to an optional, resulting in false positive warning issued by e.g. Point(1, 2).y + 0.

How can I make y optional only in __init__? The only thing that comes to mind is to split it in half:

@dataclass
class Point:
    x: int
    y: int = field(init=False)
    _y: InitVar[int | None]

    def __post_init__(self, _y: int | None):
        self.y = _y if _y is not None else self.x

but it seems overly verbose


Solution

  • Unfortunately, this doesn't seem to be covered by any existing dataclasses functionality. Easiest solution would be to forego the generated __init__ and just write it yourself:

    from dataclasses import dataclass
    
    @dataclass
    class Point:
        x: int
        y: int
    
        def __init__(self, x: int, y: int | None = None):
            self.x = x
            self.y = self.x if y is None else y
    

    Sure, dataclasses didn't do the work of generating the __init__ for you, but the incremental work here isn't much, and you still benefit from the other features and auto-generated code (__eq__, __repr__, optional rich comparison and hashing, optional automatic slot generation, etc.) it provides.

    __post_init__ exists when the default generated __init__ mostly does what you want, and just needs some tweaking, but if __init__ can't do the job right, well, you can still write it yourself. The decorator will not generate a __init__ for you if you provide one yourself, so things just work without needing to tweak further (e.g. you don't have to manually pass init=False to the @dataclass decorator).