class constructors: class vs. instance defaults

Carlos Ribeiro carribeiro at gmail.com
Thu Oct 7 01:10:52 CEST 2004


On Wed, 6 Oct 2004 16:39:50 -0600, Erik Johnson <ej
<at at bag.python.org>wellkeeper <"dot>com"@bag.python.org> wrote:
> ...
> <snip>
> ...
>     You might be surprised to find out that all the Bar objects end up
> sharing the same foo_d dictionary.

Yes, they will share. And you explanation is correct. The default
arguments are evaluated only once, when the def() is executed.

Now an explanation on what I mean when I say 'the def() is executed'.
It took me a while to really 'get' it, and I would not be surprised if
anyone ever made the same mistake.

-- When Python reads you code, it compiles it to bytecode. That's the
first step. It will always do it before execution.

-- It then executes your code -- *the entire code*. It includes class
definitions and function definitions.

-- In the case of class definitions, it really executes the class
statement body, but only once. After executing the class statement
body, it creates a class with all the symbols defined there. It's much
more flexible than a simple compilation step. When you instantiante a
object, it just reads this definition and calls the proper constructor
(__init__).

-- In the case of the def() -- or function definition -- it really
'executes the def() statement', which is not the same thing as to say
that it will execute its body. Understand? The def statement has an
effect -- it creates a function object, and associates it with the
code of the function. The default arguments are evaluated at this time
and also stored as part of the function object (you can do all types
of nice stuff with function objects, if that's something you like...
but I'll leave that for other opportunity).

-- Of course, the function body is executed only when you call it, by
referring to the function object.

That's it, in a nutshell.
 
> class Bar:
> 
>     def __init__(self, foo_d=None)
>         if foo_d:
>             self.foo_d = foo_d
>         else:
>             self.foo_d = {}

Well, it works. I don't see anything wrong with it. Youonly need to
take care with objects that are mutable. This includes lists and
dicts, but _not_ ints, floats or strings. These you can use as
defaults just fine, without fear.

... but you can try this code also:

self.foo_d = foo_d or {}

It works, and it's short-circuiting -- the new dict will only be
constructed if foo_d is None.

>      This works. I reasoned that, unlike above, the {} in this definition is
> not executed until a Bar object is instantiated. But there is a little voice
> in my head saying "This is clunky. There must be some smarter, more Pythonic
> way to do this sort of default handling without ending up with a single
> shared object"  As I said, I am pretty new to Python and I am pretty pleased
> with Python so far and expect to be using it more and more, (currently
> porting and re-writing a lot of clunky PHP). I figured I might as well take
> the time to learn this finer point now.
> 
>     This example is trivial and it may seem silly to waste time considering
> the 5 lines in my constructor, but this is just a pared down skeleton of the
> problem I ran into and I am about to write lots of classes with lots of
> members and want to call constructors with different numbers of arguments
> and have different sorts of defaults (not necessarily shared, but maybe) for
> the arguments not passed explicitly. And so, if my workaround above is not a
> very clean one, I want to learn to do it the right way before I amplify it
> 1000X.
> 
>     So... Yes? Is there a better way to do this sort of thing, or is that
> perfectly reasonable code?
> 
>     Thanks for taking the time to read my post! :)
> 
> -ej
> 
> --
> http://mail.python.org/mailman/listinfo/python-list
> 


-- 
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: carribeiro at gmail.com
mail: carribeiro at yahoo.com



More information about the Python-list mailing list