parent-child object design question

Ben Finney bignose+hates-spam at benfinney.id.au
Tue Jan 30 01:46:51 EST 2007


"manstey" <manstey at csu.edu.au> writes:

> I have two classes. The first one wraps around an old-style class
> called oref
>
> Class CacheClass(object):
>     def __init__(self, obj):
>         self.__data = obj
>     def __getattr__(self, attr):
>         return getattr(self.__data, attr)

I presume the 'obj' argument to this class's __init__ method contains
the 'oref' instance.

It's a little confusing to call the argument to __getattr__ "attr",
since it's actually the *name* of the attribute to be accessed, not
the attribute itself.

> The second class is much the same, also wrapping, but has some
> attributes.
>
> class CacheProperty(object):
>     def __init__(self, obj, dicProperties={}):

Setting a container class as a default argument is prone to error; the
default argument gets created once, when the 'def' statement is
executed. Better to default to None, and trigger on that inside the
function.

>         self.__data = obj
>         lisProperties=[]
>         for key, val in dicProperties.iteritems():
>             setattr(self, key, val)
>             lisProperties.append(key)
>         self.Properties = lisProperties

The name of this class doesn't really tell me what an instance of the
class *is*. Is it a "cache property", as the name seems to indicate?
If so, why does a "cache property" instance itself contain a list of
properties?

>     def __getattr__(self, attr):
>         return getattr(self.__data, attr)
>
> Here is my code:
>
> >>> MyClass = CacheClass(oref)

This is a confusing example; MyClass implies that the object is a
class, but this is now an instance of CacheClass, not a class.

Also, it's conventional in Python to name classes in TitleCase, but to
name instances (and functions) starting with lowercase.

> >>> MyProperty = CacheProperty(oref2,{'Name':'Surname', 'Value':'Peter'})
> >>> setattr(MyClass,MyProperty.Name,MyProperty)

This might be a good approach if you didn't know you were going to
associate the object MyClass with the object MyProperty at the
creation of MyProperty. However:

> Now, the problem is that I want a method MyClass.MyProperty.Save()
> to save the value of MyClass.MyProperty to the database backend on
> disk, but the code for this is part of the wrapped oref code, and
> thus is invoked only by
> MyClass.set(MyProperty.Name,MyProperty.Value).

This implies that there's a definite "each CacheProperty instance is
associated with exactly one CacheClass instance" invariant.

If that's true, the best thing to do is to pass MyClass to the
constructor for the CacheProperty class, and have each instance set
the relationship in the __init__ method.

I don't know what a descriptive term for the relationship between the
CacheClass instance and the CacheProperty instance is, so I'm going to
use "parent"; you should choose something more descriptive.

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

        def _bind_to_parent(self, parent):
            setattr(parent, self.Name, self)

        def _accumulate_properties(self, properties):
            self.properties = []
            for key, val in properties.iteritems():
                setattr(self, key, val)
                self.properties.append(key)

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

-- 
 \         "Marriage is a wonderful institution, but who would want to |
  `\                     live in an institution."  -- Henry L. Mencken |
_o__)                                                                  |
Ben Finney




More information about the Python-list mailing list