I want to declare dataclass field on 2 lines, one for type and second for default value. This will improve readability in cases where type name is too long, and whole line will exceed length limit.
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int
y = field(default_factory=lambda: 0)
p = Point(5)
assert p.x == 5 and p.y == 0
Running this with Python 3.12.2 does not throw any error, but mypy 1.15.0 throws the following error:
test.py:10: error: Missing positional argument "y" in call to "Point" [call-arg]
How I can accomplish multi-line field declaration without using line breaks?
The simplest way is probably to make a shorter type alias for your long type name. Otherwise you can split the line when between [] or (), but this can get rather messy looking - mypy might not complain, but someone trying to read it might.
Split between brackets/parentheses.
from dataclasses import dataclass, field
@dataclass
class X:
a: list[
tuple[
float,
float,
]
] = field(
default_factory=list,
)
Alias
from dataclasses import dataclass, field
# If you need to support 3.11 or earlier
Coordinate = tuple[float, float]
# Otherwise you can use a type statement
# type Coordinate = tuple[float, float] # 3.12 or later
@dataclass
class X:
a: list[Coordinate] = field(default_factory=list)
What I want specifically accomplish is namespace-like class for related classes.
I want to do this to reduce the number of imports, so I only need to import one class instead of many. Also I don’t want to put these classes in separate files, as this would result in a large number of small files being created.
I want to keep the class definition self-contained, without creating aliases externally.
from dataclasses import dataclass, field
from enum import Enum
@dataclass
class Namespace:
class Color(Enum):
black = 'black'
red = 'red'
@classmethod
def getDefault(cls) -> 'Namespace.Color':
return cls.black
@dataclass
class Point:
x: int
y: int
y = field(default_factory=lambda: 0)
@dataclass
class ColorPoint:
point: 'Namespace.Point'
color: 'Namespace.Color'
color = field(default_factory=lambda: Namespace.Color.getDefault())
p = Namespace.ColorPoint(Namespace.Point(5))
assert p.point.x == 5 and p.point.y == 0
assert p.color == Namespace.Color.black
Mypy errors:
test.py:34: error: Missing positional argument "color" in call to "ColorPoint" [call-arg]
test.py:34: error: Missing positional argument "y" in call to "Point" [call-arg]
Can’t you achieve all this by using the file as the namespace?
If you have a file called Namespace.py containing within it
class Color(Enum):
black = 'black'
red = 'red'
@classmethod
def getDefault(cls) -> 'Namespace.Color':
return cls.black
@dataclass
class Point:
x: int
y: int
y = field(default_factory=lambda: 0)
@dataclass
class ColorPoint:
point: 'Namespace.Point'
color: 'Namespace.Color'
color = field(default_factory=lambda: Namespace.Color.getDefault())
then in another file you can have
from ... import Namespace
p = Namespace.ColorPoint(Namespace.Point(5))
Of course, moving a class to a separate file solves the problem with mypy complaints. However, the issue arises when I have multiple namespace-like classes with around 100 lines of code and want to move them into separate files, as this would result in a large number of small files, making the file hierarchy much more complex.
AFAIK x: int = field(default_factory=lambda: 0) and
x: int
x = field(default_factory=lambda: 0)
are identical for dataclass. I believe that mypy should not complain about a missing positional argument.