This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: Add kwarg-only option to dataclass
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9, Python 3.8
Status: closed Resolution: duplicate
Dependencies: Superseder: Add keyword-only fields to dataclasses
View: 43532
Assigned To: eric.smith Nosy List: Prakhar Goel, alan_du, conchylicultor, eric.smith, jimbo1qaz_, pbryan, pmpp, rhettinger, ryanhiebert, wanderrful, wyz23x2, xtreak
Priority: normal Keywords: patch

Created on 2018-03-23 23:33 by alan_du, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 6238 closed alan_du, 2018-03-25 16:11
PR 19206 closed wanderrful, 2020-03-28 16:45
Messages (14)
msg314341 - (view) Author: Alan Du (alan_du) Date: 2018-03-23 23:33
I'd like to request a new option to the `dataclasses.dataclass` decorator to make the `__init__` keyword-only.

The two use-cases I have in mind are:

(1) Using as a dataclass big-bag-of-config. In this scenario, forcing the user to specify the keywords is a lot nicer than passing in a dozen positional parameters.

(2) Having kwarg-only parameters means that inheritance and default parameters play nicely with each other again instead of raising a TypeError.
msg314342 - (view) Author: Alan Du (alan_du) Date: 2018-03-23 23:40
If this would be accepted, I'd be happy to contribute a patch adding a `kwarg_only` option to `dataclass` (although it might take me a while since I'm not super familiar with the Python development workflow). I believe I already have the code change necessary at (although it still needs tests and documentation).
msg314343 - (view) Author: Alan Du (alan_du) Date: 2018-03-23 23:41
Err... the right link would actually be
msg314346 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2018-03-24 00:27
Changing this to 3.8: there's not enough time to add this to 3.7.
msg316784 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2018-05-16 08:26
I'd forgotten about this issue and created #33493. I'll close it. Copied here is my comment from that issue:

I've had several requests for keyword-only arguments. This is a placeholder to remind me to work on it. I have not decided if it's a good idea or not.

I propose adding a keyword_only argument to field(), defaulting to False.

I'm thinking that the basic idea would be to put all keyword-only fields at the end of the arguments to __init__, but for all other uses, leave them where they appear in the class definition. That way comparison operations, in particular, would use the fields as they appear in the class definition (which is the current behavior). Since they'd be at the end of __init__, and since order doesn't matter (they're keyword-only, after all), then this would work as expected for base classes.

That is, given:

class B:
    a: field(type=int, keyword_only=True)
    b: int

class C(B):
    c: int
    d: field(type=int, keyword_only=True)

Then B's __init__ would take (b, c, *, a, d) as its arguments, but its comparison functions would compare the tuples as (a, b, c, d).

It would be an error for a ClassVar field to be keyword-only. I think it would be okay if an InitVar field were keyword-only, but I haven't given it a lot of thought.
msg321811 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2018-07-17 10:53
There is an open PR and some discussion about this feature in attrs repo :
msg322051 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2018-07-21 00:43
+1 from me -- think would occasionally be nice to have.
msg322455 - (view) Author: jimbo1qaz_ via Gmail (jimbo1qaz_) Date: 2018-07-27 01:58
first post in are people normally scary around here?

Another usecase of keyword-only arguments is that I can mix default and non-default fields in any order. This could be achieved by either marking all fields as read-only, or by implicitly adding an asterisk before the first "conflicting" non-default argument (or by translating .../etc into an asterisk).

(Is it a bad idea to create a private fork of `pip install dataclasses` with implicit asterisk, purely for my own project?)
msg365218 - (view) Author: Michael Lee (wanderrful) * Date: 2020-03-28 16:46
Hello! I've submitted a PR based on this issue, since it seemed the most relevant from my searching of the backlog.

Here's a link:
msg374108 - (view) Author: Prakhar Goel (Prakhar Goel) Date: 2020-07-22 20:21

As another piece of evidence: I run into this problem _all the time_. Whenever I need a field in the parent with a default, I either need to add bogus default values for every field in every subclass or just skip the default in the parent.

I'd like to suggest a middle ground:
1. A field level keyword_only option which forces just that field to be a kw-only field (for every subclass as well).
2. A class level keyword_only option that does:
2a. False (default) means current behavior where classes are not keyword only (but may be forced to be by the field specific arg).
2b. True means all the fields in this class (but not the subclasses!) Would be marked as keyword only. This does affect the order of arguments in the init method but that seems acceptable given that keyword only parameters are conceptually order independent.
2c. None implies that precisely the fields with a default (or default_factory) should be keyword only fields.

msg375167 - (view) Author: wyz23x2 (wyz23x2) * Date: 2020-08-11 08:34
Since '/' was introduced in Python 3.8, support for positional parameters should be supported too.
msg380746 - (view) Author: Etienne POT (conchylicultor) * Date: 2020-11-11 10:59
Solving this would significantly improve @dataclass inheritance ( Any idea when this will land ?

Currently, it is not possible to add required argument to a child dataclass (unless hacks like duplicating attributes):

import dataclasses

class A:
  x: int = 1

class B(A):
  y: int  # ERROR: non-default argument follows default argument

Keywords-only would solve this issue:

class B(A):
  y: int = dataclasses.field(kw_only=True)

B(123, y=456)

Note: Attrs already support this:

For the behavior, I think there are two options:

class A:
  a0: int
  a1: int = field(kw_only=True)
  a2: int

class B(A):
  b0: int
  b1: int = field(kw_only=True)
  b2: int

Option 1: All attributes following `kw_only` are `kw_only` (kw_only indicates where to place '*')

A(a0, *, a1, a2)
B(a0, b0, *, a1, a2, b1, b2)

Option 2: `kw_only` are individually moved to the end

A(a0, a2, *, a1)
B(a0, a2, b0, b2, *, a1, b1)

I personally prefer Option 1, as it makes it easier to place the `*` with less boilerplate.

class Request:
  url: str
  timeout: int = field(default=-1, kw_only=True)
  verify: bool = False
  params: Optional[Dict[str, str]] = None
  cookies: Optional[Dict[str, str]] = None

Request(url, *, timeout=-1, verify=False, params=None, cookies=None)

Which looks better than:

class Request:
  url: str
  timeout: int = field(default=-1, kw_only=True)
  verify: bool = field(default=False, kw_only=True)
  params: Optional[Dict[str, str]] = field(default=None, kw_only=True)
  cookies: Optional[Dict[str, str]] = field(default=None, kw_only=True)
msg383131 - (view) Author: Etienne POT (conchylicultor) * Date: 2020-12-16 09:49
For the API, I think we could add this feature with a single new `dataclass(kw_only: bool | str)`

class A:
  a: int
  b: int
  c: int

# A(*, a, b, c)

class A:
  a: int
  b: int
  c: int

# A(a, *, b, c)

I think this would be flexible enough for all use cases, while avoiding the boilerplate from the above proposals.
msg388953 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-03-17 18:21
Closing this in favor of issue 43532, which has a slightly elaborated approach.
Date User Action Args
2022-04-11 14:58:58adminsetgithub: 77310
2021-03-17 18:21:09eric.smithsetstatus: open -> closed
superseder: Add keyword-only fields to dataclasses
messages: + msg388953

resolution: duplicate
stage: patch review -> resolved
2021-01-25 21:02:28pbryansetnosy: + pbryan
2020-12-16 09:49:49conchylicultorsetmessages: + msg383131
2020-11-11 10:59:12conchylicultorsetnosy: + conchylicultor
messages: + msg380746
2020-10-18 03:13:00ryanhiebertsetnosy: + ryanhiebert
2020-08-11 08:34:30wyz23x2setnosy: + wyz23x2

messages: + msg375167
versions: + Python 3.9
2020-07-22 20:24:41pmppsetnosy: + pmpp
2020-07-22 20:21:10Prakhar Goelsetnosy: + Prakhar Goel
messages: + msg374108
2020-03-28 16:46:51wanderrfulsetmessages: + msg365218
2020-03-28 16:45:39wanderrfulsetnosy: + wanderrful
pull_requests: + pull_request18569
2018-07-27 01:58:57jimbo1qaz_setnosy: + jimbo1qaz_
messages: + msg322455
2018-07-21 00:43:38rhettingersetnosy: + rhettinger
messages: + msg322051
2018-07-17 10:53:29xtreaksetnosy: + xtreak
messages: + msg321811
2018-05-16 08:27:07eric.smithlinkissue33493 superseder
2018-05-16 08:26:24eric.smithsetmessages: + msg316784
2018-03-25 16:11:58alan_dusetkeywords: + patch
stage: patch review
pull_requests: + pull_request5975
2018-03-24 00:27:22eric.smithsetassignee: eric.smith
2018-03-24 00:27:08eric.smithsetmessages: + msg314346
versions: - Python 3.7
2018-03-23 23:41:44alan_dusetmessages: + msg314343
2018-03-23 23:40:13alan_dusetmessages: + msg314342
2018-03-23 23:33:17alan_ducreate