No, I don’t agree, because I think what is idiomatic is essentially part of the language design.
I don’t agree and I don’t understand how you even arrived at that conclusion. “The type of python commonly written by fluent programmers” is the essence of descriptive. Prescriptivism would be if the docs or other top-down authority declared what was correct.
That’s what I’ve said in all my comments. I think I might misread your comment, and if so I apologize. If you mean by “fluent programmers” the people who read and write most of the Python code, then we agree. If you mean some higher level of programmers who dictate to others, then that’s prescriptive.
The language designers don’t produce documentation about what is idiomatic. Everything from what you put in __init__.py, how you set up annotations, whether to use decorators or inheritance, how you format docstrings, etc. None of that is specified in the docs. What is idiomatic just evolves from how people commonly write code, what they observe other people doing: socially emergent conventions.
Linguistics, the study of language, is descriptive. The study of what is idiomatic in language is by extension descriptive. The term does not apply to the way things become idiomatic, which is what you seem to be trying to explain away with descriptivism.
This isn’t entirely correct.
Firstly, PEP 8 exists - a prescriptive document. The docs also contain examples, the style of which users are likely to emulate.
Secondly, socially emergent behaviour does often end up being proscribed. To stray from what is socially acceptable means to lose social standing. Have you tried not formatting your code with Black recently?
PEP 8 “prescribes” that you don’t apply it prescriptively, apart from the Python standard library. This is in no way enforcing idioms upon all Python developers, and pretending that it does is an example of your second point.
As you say, this is enforced by public shaming. Let’s please not even suggest building public shaming into the language. All we can do is occasionally remind our more enthusiastic community members that enforcing “their” One True Way™ upon others is inappropriate (for example, by describing PEP 8 as a “prescriptive document”).
I’m not “pretending” that it does. The document is prescriptive in nature, as are all style guides. That doesn’t mean they don’t offer any leeway whatsoever. PEP 8’s reach has historically extended far beyond the standard library and has been referenced widely in support of community decisions. This is the way things are, as opposed to how we might like them to be.
I don’t know how you might’ve possibly read my comment as somehow suggesting prescription or social exclusion becoming part of Python.
Maybe it’s time to write down a PEP to remove the ~bool operator: summarize discussions about this change and write down the rationale. A PEP is an useful document to communicate the change and its rationale. The PEP doesn’t have to be long. If the PEP is rejected, at least we would have all rationale written down.
Is there any volunteer to write such PEP?
As the one bringing up the idea of removing ~bool, I could take on the task of condensing the discussion here to a PEP. Not being a core developer, I’d need a sponsor. And I’d need some help in formulating the exact proposal. I can lay out the background and discuss the advantages and disadvantages of different ways forward, but since there are various opinions, I’m not clear what the actual proposal should be.
I just noticed that this deprecation was actually added in 3.12, back in May of last year. I hadn’t realised, based on the original post here and the discussion, that this was something that was already in a released Python, and had been for some time[1].
IMO, there’s no point going through a PEP for something like this - it’s not significant enough, and it’s only for the historical record at this point.
I’d assumed without checking that it was targetting 3.13 ↩︎
It is well possible that the deprecation gets removed again (possibly even for 3.13) and given how controversial the change has turned out to be, I think having a PEP summarizing the idea, the discussion and the SC decision to point people to in the future is a good idea.
While only tangentially related to the actual thread topic, I must object to this statement. Python is an excellent language for low-level bit manipulation, and contains many tools related to such tasks, such as the buffer protocol and the struct module.
I use Python almost exclusively for such tasks. Most of my work revolves around interfacing with embedded devices over serial connections, using libraries such as pyserial and python-can. A change to hide bitwise operators behind an import would break just about every Python program I have ever written.
Just because you don’t use bitwise operators regularly does not make them unidiomatic. They are essential tools in any hardware adjacent application, and the idea that Python is not suitable for such tasks is not based in reality.
This seems exagerated to me. We seem to be mostly getting reactions from people who are afraid that it might break other people’s code, but we’re getting very few complaints from people whose code is being broken by the deprecation/pending removal.
In other words, the main reason it appears controversial is that some people fear it might be controversial. ![]()
I don’t think this is so much about breaking other people’s code, but rather a question of consistency.
Why is “~” special, while adding to booleans or negating one is perfectly possible ? We shouldn’t be pretending that True and False are not integers.
IMO, it’s better to let people know that booleans in Python are indeed just special integers. In the days before True and False, we simply used 1 and 0 most of the time and things worked well.
The documentation already makes this clear, but could perhaps emphasize on this a little more: Built-in Types — Python 3.12.5 documentation
Also note that having a subclass remove most of the methods of the base class is a rather weird concept to follow in object-oriented programming, so going full in on removing everything that does not necessarily make sense for boolean integers is not something we should be promoting. A cleaner solution would be to have integers derive from bools, since you’d then add a lot more methods to the base class, which is classic OOP (well IMO anyway
). Not sure whether that would be worth the trouble, though.
FWIW, I actually like the fact that I can do option * 'something' or option * -1 with option set to a boolean value. I don’t think I ever thought about using ~option. I find not option more readable (and it even works with arbitrary objects having a truth value, not just integers).
But hey, all this has already been pointed out in this discussion, so a PEP summarizing all the different views is a good idea, IMO.
In my personal opinion, I think this overestimates the impact that documentation has on the mental model of ordinary users. Most people don’t study documentation; mental models are built by just programming. I think you rarely discover that Boolean values are integers unless you use them that way. In some languages true is negative one (which would solve the ~ issue since then ~True == False and vice versa).
In classic OOP, inheritance is an “is-a” relationship, right? Do you really want
def f(x: bool) -> None: ...
to accept integers? This would really weaken the benefits of annotating something as Boolean if an integer can leak in.
But is that much worse than
def f(x: int) -> None: ...
accepting booleans?
Ideally neither of them should be accepted, but to old school C programmers, who just used to typedef int bool;, the former might make more sense.
Having used, bit ops on ints - some industries would use it a lot - I must give your solution a thumbs-down. It penalises too many.
What would be better ~ applied to a boolean to give an error unless you purposefully allow it; probably by
from __past__ import allowBitwiseNegatedBool
Strong agree. A lot of numpy was written to mimic Matlab, which is array based language and has a host of problems. Should also note that numpy logic arrays are not storing True or False despite the repr but numpy special bool_ type.
Apologies @abessman, @Paddy3118 , I did not mean to state that python should not continue to support your needs.
And I can see why, given that you must look at bit arrays, you might want to use python ints sometimes.
I’ve now run into a situation where my type checker compains due to ~bool deprecation.
specs.file_name = pd.Categorical(specs.file_name)
...
is_final_parquet = specs.file_name == "final_cache.parquet"
is_parquet = specs.file_name.str.endswith(".parquet")
is_tmp_parquet = is_parquet & ~is_final_parquet
The type checker has a bit of trouble here. Which usually would be something I’d ignore it doesn’t escape containment.
But ~is_final_parquet raises an annoying warning.
I can silence the warning by explicitly annotating is_final_parquet with its type.
It seems relevant to this thread.
I’m not requesting any changes to the Python language.
Is there a reason not to write it:
If not, it seems to me that the type checker is trying to steer you in the right direction.
specs.file_name = pd.Categorical(specs.file_name)
is_final_parquet and is_parquet are NDArray[np.bool_] (or maybe pd.Series[np.bool_]), not bool. ~ is well-defined here.