I am creating new data class in python.
@dataclass
class User(Mixin):
id: int = None
items: List[DefaultItem] = None
This items
is array of DefaultItem
objects but I need this to be multiple possible objects like:
items: List[DefaultItem OR SomeSpecificItem OR SomeOtherItem] = None
How can I do something like this in python?
CodePudding user response:
You can use typing.Union
for this.
items: List[Union[DefaultItem, SomeSpecificItem, SomeOtherItem]] = None
And if you are on Python 3.10, they've added a convenient shorthand notation:
items: list[DefaultItem | SomeSpecificItem | SomeOtherItem] = None
Also just as a note: If items
is allowed to be None
, you should mark the type as Optional
.
CodePudding user response:
Also, a note that in Python 3.10, you can also pass the kw_only
parameter to the @dataclass
decorator to work around the issue which I suspect you're having, wherein all fields in a subclass are required to have a default value when there is at least one field with a default value in the superclass, Mixin
in this case.
I added an example below to illustrate this a little better:
from dataclasses import dataclass
@dataclass
class Mixin:
string: str
integer: int = 222
@dataclass(kw_only=True)
class User(Mixin):
id: int
items: list['A | B | C']
class A: ...
class B: ...
class C: ...
u = User(string='abc', id=321, integer=123, items=[])
print(u)
Note that I've also wrapped the Union
arguments in a string, so that the expression is forward-declared (i.e. not evaluated yet), since the classes in the Union
arguments are defined a bit later.
This code works in 3.10 because the kw_only
param is enabled, so now only keyword arguments are accepted to the constructor. This allows you to work around that issue as mentioned, where you would otherwise need to define a default value for all fields in a subclass when there's at least one default field in a parent class.
In earlier Python versions than 3.10, missing the kw_only
argument, you'd expect to run into a TypeError
as below:
TypeError: non-default argument 'id' follows default argument
The workaround for this in a pre-3.10 scenario is exactly how you had it: define a default value for all fields in the User
class as below.
from __future__ import annotations
from dataclasses import dataclass, field
@dataclass
class Mixin:
string: str
integer: int = 222
@dataclass
class User(Mixin):
id: int = None
items: list[A | B | C] = field(default_factory=list)
class A: ...
class B: ...
class C: ...
u = User('abc', 123, 321)
print(u)