4.4. Dataclass KWonly

4.4.1. SetUp

>>> from dataclasses import dataclass, field, KW_ONLY

4.4.2. Problem

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     is_active: bool
...     is_staff: bool
...     is_superuser: bool

Positional only:

>>> User('Alice', 'Apricot', True, True, False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

Mixed:

>>> User('Alice', 'Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

Keyword only:

>>> User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

While this is flexible, it can lead to mistakes. For example, if the is_superuser and is_staff fields are accidentally swapped. It is very easy to do this when there are many boolean fields. The same applies to fields of other types like int or float.

4.4.3. All Fields

  • Since Python 3.10

  • Mark all fields as keyword-only

  • kw_only=False by default

If true, then all fields will be required to be keyword-only. If a field is set as keyword-only, then the only affect is that the __init__() parameter generated from a keyword-only field must be specified with a keyword when __init__() is called. There is no effect on any other aspect of dataclasses.

>>> @dataclass(kw_only=True)
... class User:
...     firstname: str
...     lastname: str
...     is_active: bool
...     is_staff: bool
...     is_superuser: bool

Positional only:

>>> User('Alice', 'Apricot', True, True, False)
Traceback (most recent call last):
TypeError: User.__init__() takes 1 positional argument but 6 were given

Mixed:

>>> User('Alice', 'Apricot', is_active=True, is_staff=True, is_superuser=False)
Traceback (most recent call last):
TypeError: User.__init__() takes 1 positional argument but 3 positional arguments (and 3 keyword-only arguments) were given

Keyword only:

>>> User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

4.4.4. Particular Field

  • Since Python 3.10

  • keyword-only

If true, this field will be required as keyword-only. This is used when the generated __init__() method's parameters are computed.

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     is_active: bool = field(kw_only=True)
...     is_staff: bool = field(kw_only=True)
...     is_superuser: bool = field(kw_only=True)

Positional only:

>>> User('Alice', 'Apricot', True, True, False)
Traceback (most recent call last):
TypeError: User.__init__() takes 3 positional arguments but 6 were given

Mixed:

>>> User('Alice', 'Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

Keyword only:

>>> User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

4.4.5. Following Fields

  • Since Python 3.10

  • from dataclasses import KW_ONLY

Any fields after a pseudo-field with the type of KW_ONLY are required as keyword-only fields. Note that a pseudo-field of type KW_ONLY is otherwise completely ignored. This includes the name of such a field. By convention, a name of _ is used for a KW_ONLY field.

SetUp:

>>> from dataclasses import KW_ONLY

Definition:

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     _: KW_ONLY
...     is_active: bool
...     is_staff: bool
...     is_superuser: bool

Positional only:

>>> User('Alice', 'Apricot', True, True, False)
Traceback (most recent call last):
TypeError: User.__init__() takes 3 positional arguments but 6 were given

Mixed:

>>> User('Alice', 'Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)

Keyword only:

>>> User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)
User(firstname='Alice', lastname='Apricot', is_active=True, is_staff=True, is_superuser=False)