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
interned along
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
__defaults__
and __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 func_*
ones. In Python 3, all of those were renamed to __<name>__
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 co_consts
and __defaults__
.
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__[0] is a.__code__.co_consts[1] 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
chapter, sort
of implicitly (because it lists the __defaults__
field as
writable, and what other effects would that have).
|
|