When developing software, you should develop APIs and user interfaces that follow the Principle of Least Astonishment (POLA). Basically, you want to develop software that behaves in a way that most users expect. A surprised user is either unhappy or creating buggy software.
During some recent Python development, I spent an hour chasing down a bug. When I found the problem, I was surprised.
What do you think this code prints?
def f(a=[]):
a.append(3)
return len(a)
print(f())
print(f())
print(f())
If you are like me, you expect to see:
In reality, you see:
How can this be? Calling f()
is equivalent to calling f(a=[])
.
A slight change to the code illustrates what is happening.
def f(a=[]):
a.append(3)
print(f"A={a}")
return len(a)
print(f())
print(f())
print(f())
Here, you can see that calling f()
is not equivalent to calling f(a=[])
! The same default argument object is used for every invocation of f()
. This is equivalent to:
def f(a=[]):
a.append(3)
return len(a)
b = []
print(f(b))
print(f(b))
print(f(b))
For me, this violates the Principle of Least Astonishment (POLA) and cost me an hour of debugging.
Now that we understand what Python is doing, the function can be rewritten to behave as expected.
def f(a=None):
if a is None:
a = []
a.append(3)
return len(a)
print(f())
print(f())
print(f())
Now that you know what to watch out for, hopefully you can avoid the same pitfall. Does this design decision in Python surprise you? Are there any other design decisions in Python that you find surprising? Let us know in our Slack community.
Source link
lol