Skip to content

Add kwarg-only option to dataclass #77310

Description

@alanhdu
mannequin
BPO 33129
Nosy @rhettinger, @ericvsmith, @pmp-p, @pbryan, @ryanhiebert, @alanhdu, @tirkarthi, @wyz23x2, @wanderrful, @Conchylicultor
PRs
  • bpo-33129: Add kwarg-only option to dataclass #6238
  • bpo-33129: Add kwarg support for dataclass' generated init method #19206
  • Superseder
  • bpo-43532: Add keyword-only fields to dataclasses
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/ericvsmith'
    closed_at = <Date 2021-03-17.18:21:09.137>
    created_at = <Date 2018-03-23.23:33:17.883>
    labels = ['3.8', 'type-feature', 'library', '3.9']
    title = 'Add kwarg-only option to dataclass'
    updated_at = <Date 2021-03-17.18:21:09.136>
    user = 'https://github.com/alanhdu'

    bugs.python.org fields:

    activity = <Date 2021-03-17.18:21:09.136>
    actor = 'eric.smith'
    assignee = 'eric.smith'
    closed = True
    closed_date = <Date 2021-03-17.18:21:09.137>
    closer = 'eric.smith'
    components = ['Library (Lib)']
    creation = <Date 2018-03-23.23:33:17.883>
    creator = 'alan_du'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 33129
    keywords = ['patch']
    message_count = 14.0
    messages = ['314341', '314342', '314343', '314346', '316784', '321811', '322051', '322455', '365218', '374108', '375167', '380746', '383131', '388953']
    nosy_count = 12.0
    nosy_names = ['rhettinger', 'eric.smith', 'pmpp', 'pbryan', 'ryanhiebert', 'alan_du', 'xtreak', 'jimbo1qaz_', 'wyz23x2', 'wanderrful', 'conchylicultor', 'Prakhar Goel']
    pr_nums = ['6238', '19206']
    priority = 'normal'
    resolution = 'duplicate'
    stage = 'resolved'
    status = 'closed'
    superseder = '43532'
    type = 'enhancement'
    url = 'https://bugs.python.org/issue33129'
    versions = ['Python 3.8', 'Python 3.9']

    Activity

    1. alanhdu commented on Mar 23, 2018

      alanhdumannequin
      MannequinAuthor

      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.

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Mar 23, 2018
    3. alanhdu commented on Mar 23, 2018

      alanhdumannequin
      MannequinAuthor

      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 alanhdu@fa35b39 (although it still needs tests and documentation).

    4. alanhdu commented on Mar 23, 2018

      alanhdumannequin
      MannequinAuthor

      Err... the right link would actually be https://github.com/alanhdu/cpython/tree/bpo-33129.

    5. ericvsmith commented on Mar 24, 2018

      @ericvsmith
      Member

      Changing this to 3.8: there's not enough time to add this to 3.7.

    6. self-assigned this
      on Mar 24, 2018
    7. ericvsmith commented on May 16, 2018

      @ericvsmith
      Member

      I'd forgotten about this issue and created bpo-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:

      @dataclass
      class B:
          a: field(type=int, keyword_only=True)
          b: int
      
      @dataclass
      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.

    8. tirkarthi commented on Jul 17, 2018

      @tirkarthi
      Member

      There is an open PR and some discussion about this feature in attrs repo : python-attrs/attrs#281

    9. rhettinger commented on Jul 21, 2018

      @rhettinger
      Contributor

      +1 from me -- think would occasionally be nice to have.

    10. jimbo1qaz commented on Jul 27, 2018

      jimbo1qazmannequin
      Mannequin

      first post in bugs.python.org... 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?)

    11. wanderrful commented on Mar 28, 2020

      wanderrfulmannequin
      Mannequin

      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: #19206

    12. PrakharGoel commented on Jul 22, 2020

      PrakharGoelmannequin
      Mannequin

      Hi,

      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.

      Thoughts?

    13. wyz23x2 commented on Aug 11, 2020

      wyz23x2mannequin
      Mannequin

      Since '/' was introduced in Python 3.8, support for positional parameters should be supported too.

    14. conchylicultor commented on Nov 11, 2020

      conchylicultormannequin
      Mannequin

      Solving this would significantly improve @DataClass inheritance (https://bugs.python.org/issue39300). 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
      
      @dataclasses.dataclass
      class A:
        x: int = 1
      
      
      @dataclasses.dataclass
      class B(A):
        y: int  # ERROR: non-default argument follows default argument
      

      Keywords-only would solve this issue:

      @dataclasses.dataclass
      class B(A):
        y: int = dataclasses.field(kw_only=True)
      
      
      B(123, y=456)
      

      Note: Attrs already support this: https://www.attrs.org/en/stable/examples.html#keyword-only-attributes

      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)
      
    15. conchylicultor commented on Dec 16, 2020

      conchylicultormannequin
      Mannequin

      For the API, I think we could add this feature with a single new dataclass(kw_only: bool | str)

      @dataclasses.dataclass(kw_only=True)
      class A:
        a: int
        b: int
        c: int
      
      # A(*, a, b, c)
      
      
      @dataclasses.dataclass(kw_only='b')
      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.

    16. ericvsmith commented on Mar 17, 2021

      @ericvsmith
      Member

      Closing this in favor of bpo-43532, which has a slightly elaborated approach.

    17. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    3.8 (EOL)end of life3.9 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions