Absurd Python quiz time 🐍🧩⏲️

Guess what this prints. Then try it out from a #Python REPL.

def add_sum(nums=[2, 1]):
nums.append(nums[-1] + nums[-2])
return nums

print(add_sum() or add_sum(), add_sum() and add_sum())
print(*add_sum(), *add_sum())

If you don't have a Python interpreter handy, click the Run button here: https://pym.dev/p/2wdgp

#pythonoddity

A few Python oddities rolled into one - Python Pastebin - Python Morsels

A few Python oddities rolled into one

There are multiple Python gotchas in this one.

The first is involves a mutable default value.

>>> def f(a=[]):
... a.append(1)
... return a
...
>>> f()
[1]
>>> f()
[1, 1]

Default values are evaluated just ONCE at function definition time.

Mutable defaults become a problem if/when the value is changed OR even if it's returned (since the caller could change our default).

The gotcha in our case is from mutating the passed value in our function.

The next gotcha involves short-circuiting.

>>> a = [2, 1, 3, 4, 7, 11]

This pops one value because x() or y() won't run y() if x() evaluates to something truthy:
>>> a.pop() or a.pop()
11
>>> a
[2, 1, 3, 4, 7]

This pops twice though:
>>> a.pop() and a.pop()
4
>>> a
[2, 1, 3]

And note that "a or b" and "a and b" will return the actual *value* (which may be the booleans True or False or might be some other value!)

The last gotchas involve order of evaluation in two different ways.

>>> a = [2, 1, 3, 4, 7]

When calling print, each nested expression needs to be evaluated first.

For example:
>>> print(a, a.pop(), a, a.pop())
[2, 1, 3] 7 [2, 1, 3] 4

pop ran twice, so both "a" are the same.

Slight aside: those two "a" values are the same because variables don't contain objects in Python but instead contain pointers (a.k.a. references) to objects: https://pym.dev/pointers

Variables and objects in Python

Unlike many programming languages, variables in Python are not buckets which "contain" objects. In Python, variables are pointers that "point" to objects.

Okay that last line is a bit different.

>>> a = [2, 1, 3, 4, 7]

When we run this:

>>> print(*a, "|", a.pop(), "|", *a)
2 1 3 4 7 | 7 | 2 1 3 4

*a is run in order to unpack a into separate arguments.

Then a.pop is run.

Then *a is run again.

That * unpacks a immediately!