Mismatch between assert's semantics and how it's used (-O, -OO, disable)

I actually think asserts are harder on your brain than if-raise, and I felt this especially when refactoring a codebase that was using asserts to if-raise (not in Python; those asserts were guaranteed to run in production). Here’s why:

if expr:
    print("this code runs if expr is True")

while expr:
    print("this code runs while expr is True")

assert expr, "this is raised as an exception if expr is _False_"

This negative logic makes it harder to parse, compared to other cases of branches in the code. You also need to remember that and negate expr while writing a good error message for the assertion (and you do want to write a good error message, so that there’s a chance of figuring out what went wrong without a traceback).

There’s always going to be something that some people, newbies or not, may consider surprising/unintuitive/broken (mutable default arguments), or things that you need to read docs to understand (else clause in for and while loops). Some language features are just more advanced, even though they might look simple, and for those features, educating people would lead to better outcomes.