So I’ve posted a duplicate of this and hence would like to reiterate some of the points I discuss there.
I think this suggestion put together two separate needs from the language - the need for a name-value pair that can be used as such semantically; and the ability to get the variable’s name as a string in code.
Overlap with f-string debug specifier: In this sense this does tie this suggestion with the semantics for the f-string debug specifier. The repetitions found in code such as in the pickle library (as a random example):
return _Unpickler(file, fix_imports=fix_imports, buffers=buffers,
encoding=encoding, errors=errors).load()
are there because the variables and the function parameters share semantic meaning (to respond to Elis’ concern). A similar motivation is why we want the f-string debug specifier. And why should we stop there? We would like to be able to use %
formatting with name-value pairs too - specifically for logging (specifically the scenario of lazy formatting)!
Scope of variable name: It is a question - whether the name of the variable should carry significance outside its scope. I argue it’s the case anyhow in some many places, and it’d definitely be better if this is somehow indicated declaratively. It makes the code easier to read, less verbose, and facilitate refactoring. It will beckon a person doing code changes to any keyword arguments – do they still serve the same semantic purpose the call site assumes they do, by either forcing them to decouple the names or rename callees while they’re at it.
Other languages: As mentioned in other places, this facilitates a construct similar to JavaScript’s shorthand for object initialization, where {a, b, c}
would be similar to dict(a=, b=, c=)
per this suggestion (I also dislike the star syntax). I don’t know of any other language that does this, but this is an awesome, widely used feature of Javascript.
Use in Python codebase: Using a simple grep expression I found ~2000 lines of codes that would benefit from this. Probably there is more. (egrep --include '*.py' -o -r '[, ([]([a-z0-9_]+) *= *([^.]+[.]|)\1,' cpython | wc -l
)
At odds with f-string debug specifier: In my original post, I suggested that the expression a=
create a thin name-value object that would be interpretable in place of a kwarg or in an f-string. However, I do argue that a “short name” be used as the qualifying name here, rather than a long name, e.g. a.b=
should produce the name b
rather than a.b
(especially relevant for instance variables, e.g. self.b
). This is dissimilar to the way f-string debug specifier is designed to work, so possibly one can think of this name-value object as being formatted with the full expression string, but passed as a kwarg with the “short name” (if possible to resolve one, otherwise erring at parse time).
Some counter-arguments to counter-arguments:
- Too easy to overlook: concise code tends to have people looking in the right place rather than the wrong one. Inconsistencies between lhs and rhs kwargs are much easier to overlook. Any mistakes will quickly be highlighted by any static check. Plus, it’s just as easy to overlook in f-strings.
- Not knowing what it means if you don’t already know: the same can truly be said to any symbol, namely
=
it self (is it an assignment operator or an equality one?) Any symbol operators introduced to a language suffer from this disadvantage. The fact it, though, that is does not add a symbol, rather it shortens existing expressions, asdict(a=a)
becomesdict(a=)
. It could arguably bedict(a=!)
,dict(a=↫)
ordict(a==)
but this doesn’t really matter.
Finally, does it make Python better? I would say yes:
- It’s practically built-in to the language (as kwargs are dicts anyway).
- It allows one to handle a variable’s name as if it was code, which it is.
- It gives meaning to the notion of the name-value pair and may facilitate more advanced uses in the future.
- Bringing together two seemingly disparate parts of the language together - kwargs and f-string debug specifiers - will make it simpler. It also facilitate re-use of the construct in other places (e.g. lazy formatting as in logging)
- It reduces greatly the amount of code repetition, resulting in fewer possible errors, and more readable code.