Basic article about surprising things about Python

This commit is contained in:
Smaug123
2025-06-22 00:25:16 +01:00
parent 19093d968b
commit 0254b0377d

View File

@@ -0,0 +1,71 @@
---
lastmod: "2025-06-21T00:00:00.0000000+01:00"
author: patrick
categories:
- programming
date: "2025-06-21T00:00:00.0000000+01:00"
title: Some Python surprises
summary: "I am forever astonished that people describe Python as a simple language. Here are some of the things I found very surprising about it."
---
# An assertion that can pass
The following code can succeed without throwing, although it's structurally very unintuitive why:
```python
try:
with Foo():
result = blah
except ValueError:
result.baz()
else:
assert False
```
This is an entry in the "you need to know how everything is implemented before you can understand it" registry: desugaring this makes it clear how it could pass (if the `__exit__` method on the context manager `Foo` throws `ValueError`), but the syntax is practically purpose-built to make it hard to see that it can.
# You can't monkey-patch `__add__` on an instance
Special methods like `__add__` are looked up on the *class*, not the instance.
(See [Special method lookup](https://docs.python.org/3/reference/datamodel.html#special-lookup).)
Again, `a + b` *looks* simple, but is in fact weirdly complex and its semantics are highly unintuitive.
# Integers are sometimes ref-equal and sometimes not
```python
a = 10000 # big enough to escape the small integer cache
b = 10000
a is b # False
10000 is 10000 # True
```
We're seeing a cpython optimisation in the `10000 is 10000` check: instances of literals in the same statement may be reused within that statement.
Personally I expected some amount of referential transparency around the `is` statement, but it turns out not!
# Dataclasses are implemented with strings and `eval`
I was astonished - it's literally [just weaving some strings together](https://github.com/python/cpython/blob/4eab9da960d6944546baa76e3eed56b809ea8ec0/Lib/dataclasses.py#L496) and [calling `exec`](https://github.com/python/cpython/blob/4eab9da960d6944546baa76e3eed56b809ea8ec0/Lib/dataclasses.py#L498)!
I don't know it to be *incorrect*, but the sheer bluntness of the instrument amazed me.
# The six builtins
There are [six builtins in Python](https://docs.python.org/3/library/constants.html).
`True`, `False`, and `None` are unsurprising; `__debug__` is a bit odd to be one of the six privileged constants, but fine, I guess.
`NotImplemented` is bizarre and suggests whole areas that you should ignore to maintain your sanity.
Then there's `Ellipsis`, which is the object for which `...` is sugar.
It can, for some reason, be reassigned (when `True` etc can't)?
(This reassignment doesn't actually have an effect, because the syntax `...` somehow manages to evaluate to `Ellipsis` even when the name `Ellipsis` itself is being used for a global. I still don't really understand what's happening here.)
# Multiprocessing
Oh *lord* the number of problems I've seen with the passing of objects across `multiprocessing` boundaries.
# Annoying wart: violation of Liskov substitution principle with keyword args
When overriding a method on a class, you can't rename the arguments - even to underscores - because argument names are part of a method's usable API surface.
(Pyright warns about this.)
The safe way to mark a variable as unused in an overridden method is to have the first line of the method be a useless assignment, e.g. `_ = arg_name`.