parent-child object design question

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sun Feb 4 11:31:20 EST 2007


On Fri, 02 Feb 2007 13:53:21 -0800, manstey wrote:

> Hi,
> 
> There was a mistake above, and then I'll explain what we're doing:
>>>> insCacheClass = CacheClass(oref)
>>>> insCacheProperty = CacheProperty(insOref,'Chapter')
> 
> should have been
>>>> insCacheClass = CacheClass(oref)
>>>> insCacheProperty = CacheProperty(insCacheClass ,'Chapter')
> 
> Now, to answer some questions.
> 1. Cache refers to Intersystems Cache database, an powerful oo dbase
> that holds our data.
> 2. It comes with a pythonbinding, but unfortunatley the API, written
> in C as a python extension, only provides old-style classes, which is
> why we wrap the oref (an in-memory instance of a Cache class/table)
> with CacheClass. This allows us to add attributes and methods to the
> CacheClass, the most important being CacheProperty, which is a class
> corresponding to the Cache property/field classes of the dbase.

You can add attributes and methods to old-style classes.


> 3. The pythonbind also has a strange behaviour, to our view. It gets
> and sets values of its properties (which are classes), 

Are you sure you're using the word "classes" correctly? That seems ...
strange. I'm wondering whether the properties are class _instances_.


> via the
> 'parent' oref, i.e. oref.set('Name','Peter'). But we want a pythonic
> way to interact with Cache, such as, insCacheClass.Name='Peter'. and
> insOref.Name.Set().

Okay, let me see that I understand what happens. Here's some pseudo-code
of what I think you are currently doing:


oref = get_a_reference_to_Cache_database()  # or whatever
# now do work on it using the python bindings.
oref.set("Name", "Peter")
oref.set("Something", "Else")


Here's a wrapper for the bindings, so they work more pythonically:

class Oref_Wrapper(object):
    def __init__(self, oref):
        self._oref = oref  # save the oref for later use
    # Delegate attribute access to the oref
    def __setattr__(self, name, value):
        return self._oref.set(name, value)
    def __getattr__(self, name):
        return self._oref.get(name)  # or whatever the oref does
    def __delattr__(self, name):
        return self._oref.del(name)  # or whatever the oref does
        

This is how you use it:


oref = get_a_reference_to_Cache_database()  # or whatever
# re-bind the name to a wrapped version
oref = Oref_Wrapper(oref)
# now do work on it
oref.Name = "Peter"
oref.Something = "Else"


> The code above (in post 7) does this, but as I
> asked, by storing the parent instance (insCacheClass) inside each of
> its attributes (insCacheProperty1,2, etc).

Do you use insCacheClass and insCacheProperty for anything other than
making the bindings more Pythonic? Because for the life of me I can't work
out what they're supposed to do!


> 4. Another reason we want a new-style class wrapped around the oref,
> is that each oref has many methods on the server-side Cache database,
> but they are all called the same way, namely,
> oref.run_obj_method('%METHODNAME',[lisArgs]). We want to call these in
> python in the much better insCacheClass.METHODNAME(Args). E.g. we
> prefer insCacheClass.Save() to oref.run_obj_method('%Save',[None]).

Using my code above, oref.METHODNAME(args) would resolve to:

oref._oref.get("METHODNAME")(args)

which may not do what you want. 

If you know all the possible METHODNAMES, then that's easy enough to fix:
create methods at runtime. (If you don't know the methods, the problem
becomes too hard for me to work out at 3am, but will probably still be
solvable.)

Change the __init__ method above to something like this (untested):

import new

class Oref_Wrapper(object):
    def __init__(self, oref):
        self._oref = oref  # save the oref for later use
        METHODNAMES = ["Save", "Clear", "Something"]
        for name in METHODNAMES:
            function = lambda self, *args: \
                self._oref.run_obj_method("%"+name, args)
            method = new.instancemethod(function, name)
            self.__dict__[name] = method

then define the __getattr__ etc. methods as before, and (hopefully!) you
are done:

oref.Save()
oref.Clear(1, 2, 3, 7)



I hope this was of some help.



-- 
Steven.




More information about the Python-list mailing list