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
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).