Allow for positional- or keyword-only arguments in dataclasses

There are some data classes where I want to have only keyword arguments, and some where I want only positional. Take for example:

@dataclass()
class XYZ:
    x: int
    y: int
    z: int

# For this dataclass, it might make sense to force positional-only such that
# this works:
XYZ(1, 2, 3)

# But these don't:
XYZ(1, 2, z=3)
XYZ(x=1, y=2, z=3)

Similarly, and more importantly, one might want to force keyword arguments:

@dataclass()
class Credentials:
    username: str
    password: str

# With this, we should allow:
Credentials(username="mikeholler", password="hunter2")

# But maybe not allow for concern of misuse and confusion:
Credentials("mikeholler", "hunter2")
Credentials("mikeholler", password="hunter2")

After reading dataclasses.py from the 3.8 sources, it seems like there’s no way to get at the constructor parameters to write special handling logic for myself (without using init=False and writing a custom init function every time, which is an unacceptable solution in my opinion). This means the solution would have to lie within an update to dataclass itself, some syntax to say “here is where forced positional arguments and” and “here is where forced kwargs end”.

This could be, perhaps, added to the field(...) function, but implementation details aside, the ability to specify which args in a dataclass’s constructor should be positional and which should be kwargs (or both when both are allowed) would be a great quality of life improvement.

Does anybody have good ideas for a syntax that would be best for this? Anyone also think this is a good idea?

3 Likes

A note: one thing I also tried was providing my own __init__ function when @dataclass(init=True), hoping the generated init class might do it’s thing and then call mine. But, alas, it seems like it only replaces it without error (so mine is not called at all).

Might be nice to have @dataclass generate a warning or error if it tries to set the __init__ function when one already exists to avoid confusion (probably the same for __repr__, etc.)

I found a workaround to allow this using a decorator. The problem is, the decorator breaks IDE support for parameters, but otherwise works as expected. See the gist here:

I still think something like this would be nice in stdlib.

1 Like

Hi Michael,

you write:

one thing I also tried was providing my own __init__ function when @dataclass(init=True) , hoping the generated init class might do it’s thing and then call mine. But, alas, it seems like it only replaces it without error (so mine is not called at all).

Could this code perhaps help you in getting your own init method called? Let me know:

import dataclasses
@dataclasses.dataclass
class Data:
    number: int = 42
    def __post_init__(self):
        print('Hello, world!')
print(Data().number)

outputs:

Hello, world!
42

I found this section of the PEP maybe applicable:

Regards,

Marco

I would very much support a kwonly dataclass, with the most important usecase being that one could have default values for a property without default values for the next properties:

import dataclasses
@dataclasses.dataclass(kwonly=true)
class Data:
    x: int = 42
    y: str
 
    @classmethod
    def create_from_dict(cls, configdata: dict):
        return cls(**configdata)

My suggestion solves that by automatically requiring non-defaulted properties (after defaulted properties) to be keyword-only

I’ve started a discussion of this on python-ideas, beginning at Mailman 3 [Python-ideas] dataclasses: position-only and keyword-only fields - Python-ideas - python.org

2 Likes

Is there a practical use case for this? I don’t see many useful cases for keyword-only or positional-only args in the first place. It’s mostly useful when you need to cleanly separate kwargs that will be passed on to other functions, or in very particular cases like sorted(a, b, key=foo).

Subclassing a dataclass with optional field with another dataclass with required fields will give you trouble. Making those required fields keyword-only solves that issue.

On top of that, making parameters keyword-only forces better documentation when calling (instantiating in this case), and allowing developers to make the decision to use keyword-only parameters seems to me to be only a good thing.

@EpicWink : Excellent point, thanks. I think there’s no question that keyword-only fields are needed.

1 Like