Dynamic evaluation of function argument list initializer

An example where other code can break your internal logic:

# your_lib.py
class _DefaultType: pass
_default = _DefaultType()
def silly(x: int | _DefaultType = _default) -> int:
    return 0 if isinstance(x, _DefaultType) else x + 1

# After a few hundred lines of code ...

class _DefaultType: # Other careless programmer wrote this
    ...

# Now your original _DefaultType is overwritten
# And your original typecheck will always return False.
# It will be amazingly tricky to diagnose the problem
# if you don't know where to look at.

Or even:

# naughty.py
import your_lib

del your_lib._DefaultType

# Now silly() will throw NameError
# although we did nothing to the function itself.

In Contrast:

However careless a programmer is, they would not do this unless they are intentional:

def hide():
    hidden = object()
    return lambda item: item is hidden

# check() should always return False unless hacked
check = hide()

# These must be intentional, not careless
check(check.__closure__[0].cell_contents) # True
del check.__closure__[0].cell_contents
check(None) # NameError
1 Like