The Value of Keyword-Only Arguments for Readability and Robustness
A significant theme in the discussion revolves around the benefits of using keyword-only arguments, especially in the context of Python's dataclasses
. Proponents argue that forcing the use of keywords enhances readability and prevents future breaking changes by explicitly naming parameters.
"Positional arguments means a caller can use MyDataClass(1, 'foo', False), and if you remove/reorder any of these arguments, you’ll break those callers unexpectedly. By forcing callers to use MyDataClass(x=1, y='foo', z=False), you remove this risk," notes joshdavham, highlighting the safety aspect.
"This is an awesome way to prevent future breaking changes!" they add.
masklinn counters that while technically true, "normal" functions and methods can hint at parameters through naming, whereas dataclass components are more of an implementation detail.
vbezhenar draws parallels with other languages: "For Objective C, using named parameters is the only way to call methods. I don't think I read many critique about this particular aspect. IMO it's actually a good thing and increases readability quite a bit." They also mention the popularity of using objects for named parameters in JavaScript/TypeScript React codebases and IDEs adding parameter hints.
Challenges and Strategies for Interface Evolution
The discussion also delves into the practical challenges of introducing or enforcing keyword-only arguments in existing codebases, particularly when breaking changes are involved.
"While that is self evident at a technical level, it is not quite so from a clarity / documentary perspective," states masklinn, pointing out the trade-off between technical purity and human readability.
joshdavham acknowledges the difficulty: "This is an awesome way to prevent future breaking changes!... but unfortunately, adding this to an existing project would also likely result in breakings changes haha."
chipx86 proposes a systematic approach: "What we do is go through a deprecation phase. Our process is: We provide compatibility with the old signature for 2 major releases. We document the change and the timeline clearly in the docstring. The function gets decorated with a helper that checks the call, and if any keyword-only arguments are provided as positional, it warns and converts them to keyword-only." They even reference a library, housekeeping
, and a decorator called @deprecate_non_keyword_only_args
to help manage this transition, including a specific example for patching dataclass __init__
methods.
Lutger suggests an alternative philosophy: "Even better is ... just not breaking the interface, leave the code be. Add parameters at the end. Or just add a new function. Or even a new module if things get too messy for your liking. Just leave the old users in peace." They question the necessity of breaking changes, emphasizing that if a function is used, it's valuable.
ziml77 highlights a specific pitfall with dataclasses: "But the problem here is that with a dataclass you never explicitly defined any parameters. It's the dataclass annotation that defined them for the constructor based on the order of the fields. So, yes the solution is to never re-order the fields and only add new ones to the end, but this can be a surprising requirement because normally the order of fields in a class doesn't matter to users of the class."
The Quest for Shorthand and Readability in Keyword Argument Syntax
A recurring desire in the conversation is for a more concise way to specify keyword arguments when the variable name matches the parameter name, similar to JavaScript's shorthand property syntax.
"I like keyword-only arguments, but they become tedious too quickly - especially when the variable names already match (fn(x=x, y=y, z=z)). I wish Python had JavaScript’s shorthand property syntax," expresses ayhanfuat.
sweetgiorni initially points to a proposed PEP for Python 3.14 (PEP 736) with a syntax fn(x=,y=,z=)
, but later corrects that the PEP was rejected.
pansa2 confirms the rejection, and snickerdoodle12 agrees: "Thankfully so. As much as I want a shorthand, this syntax wasn't it."
vovavili laments the outcome: "By the look of it, the feeling I get is that a decent and convenient syntax proposal has been bikeshedded into rejection. Some particular form of keyword arguments at invocation would definitely make quite a few of my scripts more readable."
toolslive sees an indirect benefit in explicit naming: "it's also a gentle force towards consistent naming, ie having the same name on caller and callee site."
jampekka notes a potential conflict with Python's syntax: "JS's shorthand property syntax is lovely and elegant. Python can't really adopt it though as it clashes with the set syntax (which is such a niche use case it really shouldn't have a special syntax)." They also critique Python's approach to syntax, suggesting a preference for "explicit" ad-hoc syntax over composable primitives. They propose a workaround: "You could do something like f(**{x, y, z}) with just that. Not the prettiest, but at least it would be DRY."
data-ottawa suggests Julia's approach of a semicolon prefix in a tuple for restructuring.
dgan suggests an "ocaml-like equivalent of ~argument
".
Alternatives and Enhancements for Handling Arguments
The discussion also explores alternative ways to handle arguments, including TypedDict
, positional-only parameters, and comparing dataclasses with libraries like Pydantic.
chipx86, in response to the kwargs
issue in libraries like Boto3, suggests using TypedDict
for better clarity and validation: "Python sort of has a fix for this now, which is to use a TypedDict to define all the possible values in the **kwargs". They also propose a future ideal of kwargs: KwargsOf[_my_inner_func]
.
mvieira38 finds TypedDict
underutilized and uses them extensively. DanielVZ suggests using stubs packages for Boto3 for development and type checking. guappa and ayhanfuat also mention experiencing the kwargs
issue with other libraries like Elasticsearch's Python client.
pansa2 points to Python's built-in mechanisms for argument control: "Keyword-only arguments? Yes: [PEP 3102]. More recently, Python also added support for positional-only parameters: [PEP 570]."
est expresses a desire for easier dictionary conversion when initializing dataclasses, suggesting a mydataclass(my_dict, lint=True)
approach.
meander_water shares a lesser-known feature: "You can also explicitly specify which arguments need to be keyword only using the KW_ONLY sentinel type annotation".
Dataclasses vs. Pydantic and Other Libraries
A segment of the discussion compares Python's built-in dataclasses
with external libraries like Pydantic, particularly concerning features like data validation.
eachro questions the necessity of Pydantic "base models" over dataclasses, leading to a discussion on stdlib vs. third-party dependencies.
alfons_foobar suggests a preference for stdlib and finds dataclasses more straightforward, less prone to "magic" than Pydantic's serialization and validation features.
guappa and mvieira38 prioritize speed and size, suggesting Pydantic is a heavy dependency if validation isn't needed.
JimDabell finds Pydantic's ergonomics less appealing, preferring attrs/cattrs
as a stepping stone from dataclasses.