One of the issues with Python 3 is that there isn’t really one killer feature, it’s countless little ones. Many believe 3.6 was the first release where they added up to enough of a benefit (though f-strings are a big help for many projects).
Regardless it no longer matters, the Python 2 ecosystem is now rotting as packages drop support. Every week I have to make one or two hot fixes somewhere to forcibly pin to an old version to fix something.
For me, proper handling and distinction of Unicode vs. binary data was a game changer. I don't know if that's related to my first language being non-English, but I remember it being really important to me and a strong reason why I made the switch years ago.
And in doing so creates multiple ways to do the same thing which is SUPER SUPER annoying.
There has been a recent effort to add all these new operators that don't actually let you do anything you couldn't but now you can confuse everyone by doing it in other ways.
The bigger reasons are discoverability (as noted in the section you linked) and the ever-vague notion of Pythonic-ness.
{d1, d2} is not intuitive to a primarily-Python developer, and looks nothing like typical Python. The dict unpacking operator it uses is almost never seen outside function arguments.
F-strings are the one obvious way to do string formatting. There may be other ways, for legacy backwards compatibility reasons, but f-strings are the way to do string formatting.
I think F-strings are bad for i18n. It sucks to use them with a database of string localizations because the variable name is now embedded in dozens of translated F-strings and basically becomes immutable.
I disagree. In logging for example, ‘%s’ with the value as an argument to the logger is preferred because the formatting can be ignored if the log level is not sufficient to print.
Log formatting is a different problem than string formatting. With log formatting you pass the formatting arguments as function parameters, which is completely different from any other way you format strings.
Go back to 1.0 and we still had two ways to write strings ("abc" and 'abc'), and two ways to write not equal ("!=" and "<>").
The last died with 3.0.
But my point is that the Zen of Python must be seen as a post hoc description overlaid onto whatever the actual Python philosophy is. Aligned, certainly, but at times only roughly aligned.
So I don't see it as having sailed (with string formatting, or other specific even) but never having been there in the first place. More like, sailing in the same waters.
I think go dropped one of these (for vs while) to keep to just one approach on loops so python providing lots of ways to do same thing is something other languages targeting entry level folks are seeking to avoid
Fantastic. Especially glad they went with | over +, that’s always felt like the natural way I’ve wanted to do this. Looking forward to more set-like operators in the future!
We had a whole discussion on HN last time[1] about this, where I argued that dicts are logically subclasses of sets and therefore should share operators.
When I saw this headline I accepted my fate of typing the "wrong" operator from now on and liking Python just a tiny bit less for the inconsistency. So glad they reconsidered.
> The new operators will have the same relationship to the dict.update method as the list concatenate (+) and extend (+=) operators have to list.extend. Note that this is somewhat different from the relationship that |/|= have with set.update; the authors have determined that allowing the in-place operator to accept a wider range of types (as list does) is a more useful design, and that restricting the types of the binary operator's operands (again, as list does) will help avoid silent errors caused by complicated implicit type casting on both sides.
Would someone please explain what they mean with regard to being different from set.update, and what could lead to silent errors?
I wouldn't have thought about dict unpacking as a solution either but once suggested it seems satisfactory and I don't see how adding a new operator is more discoverable or natural than just putting this method in a more prominent place in the documentation.
Note that a | b is already a disaster if you try to use subclasses of set. In python 2.7, iirc it would return a set of a's type but without calling the constructor. In python 3 it seems to return a set (not the type of a or the type of b).
It's pretty obvious with the context of other languages, but wildly outside the norm for Python. I rarely see dict unpacking outside function signatures.
iirc the pep mostly just says that it's suboptimal because it's syntactically heavy/noisy, non-obvious and can't be overloaded in dict subclasses
---
i was curious why the two double-stars behave differently despite syntactic similarity. so i went and checked the bytecode, and it turns out they compile down to different opcodes! `{××d1, ××d2}` yields a BUILD_MAP_UNPACK, while `dict(d1, ××d2)` yields a CALL_FUNCTION_EX/CALL_FUNCTION_KW (depending on the CPython version)
You are calling the dict function, and using the normal syntax for unpacking a dictionary into kwargs. In this case, the name for kwargs must be strings.
yeah, that side of the (in)equation was pretty obvious, i was mostly interested in the `{...}` one. i admit that a bytecode listing probably isn't the best exposition, i just like digging into VM stuff :)
Yay! I was wishing for this feature just a few days ago. It's somewhat analogous to how sorted (since Python 2.4) frees us from having to tediously make copies of lists to sort them in place.
Most of the bug tracking thread was just about whether `somedictsubclass() | somedictsubclass()` should be `dict()` or `somedictsubclass()`
The latter (returns `somedictsubclass`) would cause the `|` operator to rely on the `copy()` method from `dict` which would be the only case where an operator relies on a non-double-underscores method. Based on that, two core devs were against it. The core devs prevailed, and the behaviour will be the former (returns `dict`).
It seems that just using + as the operator was reject because it's: "Too specialised to be used as the default behavior."
What does that mean? It works for lists, obviously lists don't need to worry about duplicated values, but it's kind non-intuitive that + won't work for dicts. It think many people view dicts and lists as the same general type of data structure.
Python 2 Community: We are in hell, we have to stop working on everything to upgrade to Python 3, there is no straightforward way to upgrade, many of our python 2 libraries haven't been updated, and there are tons of little bugs that are hard to fix.
Python 3 Community: Look at thing cool dictionary merging thingy!
The problem with sprinkling operator overloading all over the place in non numerical use is that you as tje reader don't get the context hints provided by method names. I think this change is bad in the overall balance.
The best way to do dictionary union is already symbolic:
{**d1, **d2}
This provides a clearer symbolic notation for dictionaries analogous to what's already available with sets. FWIW the pep discusses what this would look like as a method vs an operator:
Not sure what you're referring to because there is no "union" method/function. There is currently no non-symbolic built-in way to combine dictionaries in an expression.
You may be interested to read PEP 584's list of examples of all the real-world code the existence of this operator makes clearer:
I agree. Back in the day when I used Ruby, I remember one of the arguments for Python being their belief that there should be one way to do things. Found one reference:
> There should be one-- and preferably only one --obvious way to do it.
The choice of the "|" operator for set union comes from bitwise operations: bitwise OR works as a union operator if you are using integers as bit vectors to represent sets of boolean attributes. And it was a common idiom back in the day when people used to program in C/assembly, using words as bit vectors was a common way to save memory.
Hence "|" as set union is intuitive for people who are familiar with this application of bit vectors.
I looked up set theory a couple of places (WP[1] and Britannica[2]) and didn't find any references of the OR operator in this context.. do you have a link?
The analogue of the OR operator in set theory is the union operator. People think of them as basically the same thing because of the correspondence between a property and the set of things with that property. If A is the property of being either B or C, then the set of the things that are A is the union of the set of things that are B and the set of things that are C.