class constructors: class vs. instance defaults
Larry Bates
lbates at syscononline.com
Wed Oct 6 19:19:51 EDT 2004
Erik Johnson <ej <at> wellkeeper wrote:
> I am rather new to Python and still learning the finer points. I wanted
> to have a class which has a member dictionary of other class instances, a
> constructor that would initiate that dict with an empty dict if you didn't
> provide one, and a member function to stick more other classes into that
> dictionary.
>
> I wrote something akin to this:
>
> #! /usr/bin/python
>
> #=======================================================================
> class Bar:
>
> def __init__(self, foo_d={}):
> self.foo_d = foo_d
>
> def add_foo(self, foo):
> self.foo_d[foo.key] = foo
> #=======================================================================
>
>
> #=======================================================================
> class Foo:
> pass
> #=======================================================================
>
>
> You might be surprised to find out that all the Bar objects end up
> sharing the same foo_d dictionary.
> I certainly was.
>
>
>
>>>>from Test import *
>>>>b1 = Bar()
>>>>b2 = Bar()
>>>>dir(b1)
>
> ['__doc__', '__init__', '__module__', 'add_foo', 'foo_d']
>
>>>>b1.foo_d
>
> {}
>
>>>>b2.foo_d
>
> {}
>
>>>>f = Foo()
>>>>f.key = 'key'
>>>>b1.add_foo(f)
>>>>b1.foo_d
>
> {'key': <Test.Foo instance at 0x817821c>}
>
>>>>b2.foo_d
>
> {'key': <Test.Foo instance at 0x817821c>}
>
>
> A single, shared dictionary is definitely not what I wanted or expected
> and didn't see why it was happening. So... I struggled with that for a
> while and eventually reasoned that the {} in the argument list to my
> constructor is executed at the time of class definition, and is essentially
> a reference to an instantiated dictionary object and therefor there is a
> little empty dictionary sitting in memory somewhere prior to me ever
> instantiating a Bar object. New Bar objects constructed without providing
> their own foo_d end up sharing that one. I think this makes sense enough,
> now (if I'm missing something, speak up).
>
> So, one work-around is to do this:
>
> #=======================================================================
> class Bar:
>
> def __init__(self, foo_d=None)
> if foo_d:
> self.foo_d = foo_d
> else:
> self.foo_d = {}
> #=======================================================================
>
>
> 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
>
>
I write this as:
#=======================================================================
class Bar:
def __init__(self, foo_d=None)
self.foo_d = foo_d or {}
#=======================================================================
but it is basically the same thing as others have suggested. Seems to
make it into a "one-liner" and I think the overhead is quite small to
evaluate the boolean 'or'.
Larry Bates
More information about the Python-list
mailing list