Or, if you need a "static" variable for other purposes, the usual alternative is to just use a global variable, but if for some reason you can't (or you don't want to) you can use the function itself!
def f():
if not hasattr(f, "counter"):
f.counter = 0
f.counter += 1
return f.counter
print(f(),f(),f())
> 1 2 3
even 0 = true
even n = odd n-1
odd 0 = false
odd n = even n-1
I fed a C version of this (with unsigned n to keep the nasal daemons at bay) to clang and observed that it somehow manages to see through the mutual recursion, generating code that doesn't recurse or loop.
In Python you'd maybe think, smart, then my counter is a fast local variable. But you look up (slow) the builtin hasattr and the module global f anyway to get at it. :)
I looked at python dis output before writing this, you can look at how it specializes in 3.11. But there's also 4 occurences of LOAD_GLOBAL f in the disassembly of this function, all four self-references to f go through module globals, which shows the kind of "slow" indirections Python code struggles with (and can still be optimized, maybe?)
You could scratch your head and wonder why even inside itself, why is the reference to the function itself going through globals? In the case of a decorated or otherwise monkeypatched function, it has to still refer to the same name.