Where the default values for Python function arguments are stored
One of the things that surprised me when I was researching yesterday's
entry on using
is with literals was that I
couldn't work out where (C)Python kept the default values for
function arguments. In the end I didn't need to know for sure because
I was able to demonstrate that function default argument values are
with constants used in the function code, but it bugged me. Today
I worked it out and now I can show some more interesting things.
In the end, finding the answer was as simple as
reading the documentation for the
inspect module. Constants
used in code are found in the
co_consts attribute on code objects,
but the default values for function arguments are found in the
__kwdefaults__ attributes of function objects.
Once I thought about it this split made a lot of sense. Code objects
can come from many sources (for instance,
compile()) and not all of
those sources actually have any concept of arguments (with or without
default arguments). So attaching 'default values for function arguments'
to code objects would be wrong; they need to go on function objects,
where they make sense.
(The difference in naming style is (likely) due to Python 3 limiting
how much code it was willing to break and force people to update.
In Python 2, function default argument values are exposed in a
func_defaults attribute, along with a number of other
ones. In Python 3, all of those were renamed to
versions, while code objects had their attribute names left alone.
If Python was being recreated from scratch today, I suspect that
code objects would have only
__<name>__ attributes too.)
This means that CPython's constant interning is being somewhat more
clever than I expected. Since default argument values don't go in
co_consts, CPython is somehow building an overall constant pool,
then (re)using it for both
CPython is definitely making use of the same objects in these two
attributes, which I can now demonstrate in a different and more
direct way than I did in the last entry:
>>> def a(b=3000): ... return b == 3000 >>> a.__defaults__ (3000,) >>> a.__code__.co_consts (None, 3000) >>> a.__defaults__ is a.__code__.co_consts True
One of the minor uses of function
__defaults__ that I can see is to
examine the current state of function default argument values, just in
case someone has managed to mutate one of them.
PS: In reading the CPython code, I discovered that you can actually
set new values for these default argument values by assigning to
__defaults__. This is described in the Python data model
of implicitly (because it lists the
__defaults__ field as
writable, and what other effects would that have).