parent-child object design question

Steven D'Aprano steve at REMOVEME.cybersource.com.au
Wed Jan 31 02:24:22 EST 2007


On Tue, 30 Jan 2007 21:15:53 -0800, manstey wrote:

> Hi Ben,
> 
> Could I also do something like the following? What does it mean to
> store the parent class as a private variable in the child class?

What it means is that references to "self.__data" (note the TWO leading
underscores) in your code below are mangled by Python to be
"self._CacheProperty__data__" instead.

This gives you a weak form of "private" variable, good for annoying people
and yourself, possibly okay to help prevent many accidental name clashes,
but not much else.

On the other hand, references to "self._parent" (note only ONE leading
underscore) is a convention. It is like putting a comment "Private, don't
touch" in your code. It isn't enforced by Python. That's usually a good
thing.


> class CacheProperty(object):
>         def __init__(self, obj, parent, properties=None):
>             self.__data = obj
>             self._parent = parent
>             if properties is None:
>                 properties = {}
>             self._accumulate_properties(properties)
> 
> 
>         def _accumulate_properties(self, properties):
>             self.properties = []

Probably better to put that in the __init__ method, otherwise if somebody
runs instance._accumulate_properties(...) again, it will have the
side-effect of throwing away whatever was already in instance.properties.

Some people might argue that if someone runs a private method like
_accumulate_properties, they deserve whatever bad things happen to them.
But I say, well, sure, but why *design* your method to break things when
called twice? Who knows, maybe you'll decide you want to call it twice
yourself.


>             for key, val in properties.iteritems():
>                 setattr(self, key, val)
>                 self.properties.append(key)

If I am reading this correctly, self.properties is almost the same as
self.__dict__.keys() only missing some values.


>         def __getattr__(self, name):
>             return getattr(self.__data, name)

Okay, let's see how this works.

child = CacheProperty([1,2,3], PARENT, {"food": "eggs", "colour": "green"})

# child.__data is the list [1,2,3]
# child._parent is PARENT (whatever that is)
# child.properties is the list ['food', 'colour']

Now, if I say:

child.__len__()
=> returns 3

child.index(2)
=> returns 1

That's just straight-forward delegation to the "obj" argument stored in
data. But when I say:

child.set('foo')

expecting to have the following method called:

>         def set(self, property):
>             return self._parent.set(property)

oops! It gets delegated to obj instead of the parent.

> the set function allows us to call the parent set function within the
> child, which is what we need to do.

Why not do this?

instance._parent.set('foo')

No mess, no fuss, bug-free, and self-documenting.


-- 
Steven D'Aprano 




More information about the Python-list mailing list