473,715 Members | 6,082 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

parent-child object design question

Hi,

I am having trouble designing my classes.

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

The second class is much the same, also wrapping, but has some
attributes.

class CacheProperty(o bject):
def __init__(self, obj, dicProperties={ }):
self.__data = obj
lisProperties=[]
for key, val in dicProperties.i teritems():
setattr(self, key, val)
lisProperties.a ppend(key)
self.Properties = lisProperties
def __getattr__(sel f, attr):
return getattr(self.__ data, attr)

Here is my code:
>>MyClass = CacheClass(oref )
MyProperty = CacheProperty(o ref2,{'Name':'S urname', 'Value':'Peter' })
setattr(MyCla ss,MyProperty.N ame,MyProperty)
Now, the problem is that I want a method MyClass.MyPrope rty.Save() to
save the value of MyClass.MyPrope rty 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(MyP roperty.Name,My Property.Value) .

How can I access this method inside the MyProperty class?

I hope this is clear!

regard,s
matthew

Jan 30 '07 #1
11 2855
"manstey" <ma*****@csu.ed u.auwrites:
I have two classes. The first one wraps around an old-style class
called oref

Class CacheClass(obje ct):
def __init__(self, obj):
self.__data = obj
def __getattr__(sel f, 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(o bject):
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.i teritems():
setattr(self, key, val)
lisProperties.a ppend(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__(sel f, 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(o ref2,{'Name':'S urname', 'Value':'Peter' })
setattr(MyClas s,MyProperty.Na me,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.MyPrope rty.Save()
to save the value of MyClass.MyPrope rty 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(MyP roperty.Name,My Property.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(o bject):
def __init__(self, obj, parent, properties=None ):
self.__data = obj
self._bind_to_p arent(parent)
if properties is None:
properties = {}
self._accumulat e_properties(pr operties)

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

def _accumulate_pro perties(self, properties):
self.properties = []
for key, val in properties.iter items():
setattr(self, key, val)
self.properties .append(key)

def __getattr__(sel f, 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

Jan 30 '07 #2
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?

class CacheProperty(o bject):
def __init__(self, obj, parent, properties=None ):
self.__data = obj
self._parent = parent
if properties is None:
properties = {}
self._accumulat e_properties(pr operties)
def _accumulate_pro perties(self, properties):
self.properties = []
for key, val in properties.iter items():
setattr(self, key, val)
self.properties .append(key)
def __getattr__(sel f, name):
return getattr(self.__ data, name)

def set(self, property):
return self._parent.se t(property)

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

Jan 31 '07 #3
"manstey" <ma*****@csu.ed u.auwrites:
Could I also do something like the following?
I can't immediately see a problem with the code you posted. Does it do
what you want it to do?
What does it mean to store the parent class as a private variable in
the child class?
I don't understand this question. It's up to you what it means; I
leave it to the people who know the purpose of the program to decide
whether "parent" is even an appropriate term.

--
\ "I took a course in speed waiting. Now I can wait an hour in |
`\ only ten minutes." -- Steven Wright |
_o__) |
Ben Finney

Jan 31 '07 #4
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.__dat a" (note the TWO leading
underscores) in your code below are mangled by Python to be
"self._CachePro perty__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._pare nt" (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(o bject):
def __init__(self, obj, parent, properties=None ):
self.__data = obj
self._parent = parent
if properties is None:
properties = {}
self._accumulat e_properties(pr operties)
def _accumulate_pro perties(self, properties):
self.properties = []
Probably better to put that in the __init__ method, otherwise if somebody
runs instance._accum ulate_propertie s(...) again, it will have the
side-effect of throwing away whatever was already in instance.proper ties.

Some people might argue that if someone runs a private method like
_accumulate_pro perties, 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.iter items():
setattr(self, key, val)
self.properties .append(key)
If I am reading this correctly, self.properties is almost the same as
self.__dict__.k eys() only missing some values.

def __getattr__(sel f, 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.propertie s 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.se t(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._paren t.set('foo')

No mess, no fuss, bug-free, and self-documenting.
--
Steven D'Aprano

Jan 31 '07 #5
"Steven D'Aprano" <st***@REMOVEME .cybersource.co m.auwrites:
def _accumulate_pro perties(self, properties):
self.properties = []

Probably better to put that in the __init__ method, otherwise if
somebody runs instance._accum ulate_propertie s(...) again, it will
have the side-effect of throwing away whatever was already in
instance.proper ties.
That's the point of naming it with a leading underscore. You've
already explained that this is convention for "Private, don't use".
Some people might argue that if someone runs a private method like
_accumulate_pro perties, they deserve whatever bad things happen to
them. But I say, well, sure, but why *design* your method to break
things when called twice?
Exactly the same could be said for calling the __init__ method twice.
Who knows, maybe you'll decide you want to call it twice yourself.
Right. In which case, it's good that it's already in a separate,
clearly-named, single-purpose method.

Make the code easy to understand, not idiot-proof.

--
\ "One time a cop pulled me over for running a stop sign. He |
`\ said, 'Didn't you see the stop sign?' I said, 'Yeah, but I |
_o__) don't believe everything I read.'" -- Steven Wright |
Ben Finney

Jan 31 '07 #6
Thanks for your input. Here is my next version, which works very well,
but for one problem I explain below:

class CacheProperty(o bject):
def __init__(self, insCacheClass, name):
self.Name = name
self._bind_to_p arent(insCacheC lass)
self.__parent = insCacheClass
self.__pyValue = self.__parent.g et(name)

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

def get(self):
return self.__pyValue

def set(self, val):
self.__parent.s et(self.Name, val)
self.__pyValue = val # Set in wrapper's copy

Value = property(get, set)

def __repr__(self):
return str(self.Value)
def __str__(self):
return str(self.Value)
class CacheClass(obje ct):
def __init__(self, obj):
self.__data = obj

def getOref(self):
return self.__data
def __repr__(self):
return 'self.__data'
def __str__(self):
return str(self.__data )
def __getattr__(sel f, attr):
return getattr(self.__ data, attr)

Our code works fine as follows (oref is in-memory version of Cache oo-
dbase class, and is an old-style class that comes with set and get and
run_obj_method methods):
>>insCacheCla ss = CacheClass(oref )
insCachePrope rty = CacheProperty(i nsOref,'Chapter ')
print insOref.Chapter .Value
5
>>print insOref.Chapter .Name
'Chapter'
>>insOref.Chapt er.Value=67
print insOref.Chapter
67

However, the problem is now that I can also write:
>>insOref.Chapt er=67
but we want to disallow this, as insOref.Chapter must remain =
insProperty

We add various other instances of CacheProperty to the insOref as
well, btw.

So any idea how to prohibit this, and can the class code above be
improved? Does it matter that each instance of CacheProperty contains
self.__parent? Does it actually "contain" the parent (I realise this
is not 'parent' in usual python lingo - what would be a better term?),
or is self.__parent simply point to it somehow? I don't understand
this part of python at all!

Thanks, you are being a great help in our development.

Jan 31 '07 #7
"manstey" <ma*****@csu.ed u.auwrites:
However, the problem is now that I can also write:
>insOref.Chapte r=67
but we want to disallow this, as insOref.Chapter must remain =
insProperty
Then don't do that.

Python allows any name to be reassigned to any value, with the
attitude of "we're all consenting adults here". It's better to
document[0] the correct behaviour of
your code, rather than trying to prevent stupid mistakes.
[0]: and unit tests, that explicitly check the behaviour of the code,
and get run all the time during development of the code, are the best
way of documenting behaviour unambiguously.

--
\ "Buy not what you want, but what you need; what you do not need |
`\ is expensive at a penny." -- Cato, 234-149 BC, Relique |
_o__) |
Ben Finney

Jan 31 '07 #8
On Wed, 31 Jan 2007 20:15:44 +1100, Ben Finney wrote:
"Steven D'Aprano" <st***@REMOVEME .cybersource.co m.auwrites:
def _accumulate_pro perties(self, properties):
self.properties = []

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

That's the point of naming it with a leading underscore. You've
already explained that this is convention for "Private, don't use".
"Somebody" could easily be the class designer. This is a method that, as
written, will break the instance if it is run twice. That's not good.

>Some people might argue that if someone runs a private method like
_accumulate_pr operties, they deserve whatever bad things happen to
them. But I say, well, sure, but why *design* your method to break
things when called twice?

Exactly the same could be said for calling the __init__ method twice.
__init__ isn't a "private" method, it is a public method. A very special
public method, designed to initialise the instance, that is to set the
instance to a brand new pristine state.

Calling instance.__init __ *should* initialise the instance and throw away
whatever data was already there. (Why you would want to do this is another
question.) What else would you expect it to do? If you called __init__ and
it *didn't* initialise the instance, that would surely be a bug.

But _accumulate_pro perties as written is sort of half-man-half-duck sort
of thing: it would be useful for accumulating properties to the instance,
except that when you do so it throws away whatever properties you have
previously accumulated. Or rather, it doesn't even do that -- it simply
throws away the list of properties, but leaves the actual properties
behind. That can't possibly be good design! An inconsistent internal
state like that is a bug waiting to happen.

>Who knows, maybe you'll decide you want to call it twice yourself.

Right. In which case, it's good that it's already in a separate,
clearly-named, single-purpose method.
It isn't either clearly-named or single-purpose.

The name is misleading, for two reasons. First, "properties " has a
technical meaning in Python, and this method doesn't have anything to do
with that meaning. Secondly, even if it did, it doesn't accumulate them,
it resets the list to a new state, throwing away whatever was there before.

Nor is it single-purpose: while it *sets* a list of attribute names to a
new list of values, it *adds* those new attributes to the instance __dict__.

There are some methods that, in principle, should be run once and once
only. There's nothing inherently about accumulating attributes/properties
to an instance can only be done once. But the way the method is written,
it will break things if you try to accumulate attributes twice -- not
because of any inherent badness in doing so, but because the method breaks
the consistency between self.properties (a list) and the actual attributes
accumulated.
Make the code easy to understand, not idiot-proof.
I'm not arguing that the entire method should be moved into __init__.
*Initialising* self.properties list to [] is not logically part of
*accumulating* properties, and should be moved out of that method. That
tiny change will fix all of the problems I describe except for the
misleading name.

--
Steven D'Aprano

Feb 1 '07 #9
On Wed, 31 Jan 2007 15:09:29 -0800, manstey wrote:
Thanks for your input. Here is my next version, which works very well,
but for one problem I explain below:

class CacheProperty(o bject):
def __init__(self, insCacheClass, name):
self.Name = name
self._bind_to_p arent(insCacheC lass)
self.__parent = insCacheClass
self.__pyValue = self.__parent.g et(name)

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

def get(self):
return self.__pyValue

def set(self, val):
self.__parent.s et(self.Name, val)
self.__pyValue = val # Set in wrapper's copy

Value = property(get, set)

def __repr__(self):
return str(self.Value)
def __str__(self):
return str(self.Value)
class CacheClass(obje ct):
def __init__(self, obj):
self.__data = obj

def getOref(self):
return self.__data
That's pointless. Just get rid of the double-underscores and let the
caller refer to self.data directly.
def __repr__(self):
return 'self.__data'
What's the purpose of this? Why do completely different instances of
CacheClass have exactly the same repr?

def __str__(self):
return str(self.__data )
Pointless, because __getattr__ will have the same effect: if __str__
doesn't exist, __getattr__ will call getattr(self.__ data, "__str__") which
is equivalent to str(self.__data ).

def __getattr__(sel f, attr):
return getattr(self.__ data, attr)
Straight-forward delegation to self.__data. As near as I can see,
CacheClass(oref ) essentially does nothing different to just oref. So why
wrap oref in another class? What's the purpose of this? Why not just refer
to oref in the first place?

What makes this a "cache" class? It doesn't seem to act anything like a
cache to me. It seems to be just a wrapper around oref that doesn't really
do anything.

Our code works fine as follows (oref is in-memory version of Cache oo-
dbase class, and is an old-style class that comes with set and get and
run_obj_method methods):
>>>insCacheClas s = CacheClass(oref )
insCacheProp erty = CacheProperty(i nsOref,'Chapter ')
What is insOref and where does it come from? Is it different from oref?

Your classes are confusing, but I feel that all of the above is just a
big, complicated way of saying something like this:

insOref = something_or_ot her
insOref.some_na me = insOref

and then jumping through all sorts of hoops writing this:

insOref.some_na me.attribute

instead of just insOref.attribu te
That's what it seems like to me. Have I missed something?

>>>print insOref.Chapter .Value
5
>>>print insOref.Chapter .Name
'Chapter'
>>>insOref.Chap ter.Value=67
print insOref.Chapter
67

However, the problem is now that I can also write:
>>>insOref.Chap ter=67
but we want to disallow this, as insOref.Chapter must remain =
insProperty
Why?
We add various other instances of CacheProperty to the insOref as
well, btw.
This whole design seems incredibly byzantine to me. It would help if you
take a step back from asking technical questions "how do I do this?" and
instead tell us what you're trying to accomplish.

Why do you wrap the instances in the first place? What benefit do you get?

Here is something that you should do. Write your documentation for the
classes CacheProperty and CacheClass. The documentation should explain
what they are for, why you would use them, and how you use them. If you
can't explain what they are for and why you would use them, chances are
very very good that they don't do anything useful and you shouldn't use
them at all.


--
Steven D'Aprano

Feb 1 '07 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
3651
by: Suzanne Vogel | last post by:
Hi, Given: I have a class with protected or private data members, some of them without accessor methods. It's someone else's class, so I can't change it. (eg, I can't add accessor methods to the parent class, and I can't make some "helper" class a friend of the parent class to help in accessing the data.) Problem: I want to derive a class that has a copy constructor that properly copies those data members.
5
7666
by: Steve Richter | last post by:
In my user control I want to read the ViewState dictionary of the Parent control. But this sensible idea is not permitted by the compiler: Compiler Error Message: CS1540: Cannot access protected member 'System.Web.UI.Control.ViewState' via a qualifier of type 'System.Web.UI.Control'; the qualifier must be of type 'ASP.ItemOrderGrid' (or derived from it) Source Error:
1
4654
by: John A Grandy | last post by:
Let's say I have an .aspx Page instance derived from class "MyPage" (which of course inherits from System.Web.UI.Page). On the .aspx Page , I have "uc1", an instance of a UserControl dervived from "MyUserControl". uc1 is contained in a <td> in a <tr> in a <table> in a <td> in a <tr> of another <table> ... or some other form of deep nesting. In uc1 's code-behind I'd like to reference some properties of the parent
4
9736
by: Phil Powell | last post by:
I thought this would work but it seems to not work neither in Netscape nor in IE: <script type="text/javascript"> <!-- // OBTAINED FROM http://www.javascripter.net/faq/settinga.htm // NOTE THAT IF YOU SET days TO -1 THE COOKIE WILL BE SET TO YESTERDAY
13
3591
tpgames
by: tpgames | last post by:
What is the onload event handler needed to get # assigned to parent variable? What I tried didn't work! Code is from JS bible, page 127-8 with some minor alterations by me. Parent Page (in part) <!-- start function goNext() { var currOffset = parseInt(parent.currTitle); if (currOffset < 40) { currOffset +=1; parent.cryptogram.location.href = "http://www.tpgames.net/gaming/2/word/cipher/f/c/c" + currOffset + ".html";...
1
4551
by: IframeLearner | last post by:
Hi , I am trying to upload a file from a parent.jsp using Iframes. From Parent page. I have to save Subject, Desc, File and file name. to upload the file i am using Iframe. I want the iframe to save file in the Db and come back to parent page with file name in the form of the parent , so that i can save the parent after updaing other details. I am able to save the file in the DB and come back to parent page. While coming back...
5
2182
by: gnewsgroup | last post by:
In my user control, I would like to find a Label control in the parent page (the page that uses my user control). I need to update that Label.Text when something happens in the user control. I don't want to go through the hassle of creating events in the user control, and then let the parent handle the event. What is the easiest way to find a control in the parent page? Right now, I am simply manually traversing it from the user...
1
3539
by: SunshineInTheRain | last post by:
My project has 3 files, File1 has included master page. file1 consists of iframe1 that load file2. File2 consists of iframe2 that load file3. Javascript used on each file to resize the iframe height based on the each iframe's content height. It is work well on IE, but the error "has no properties" occured with firefox on code as below. Where both code is to get the id of iframe on file1....
1
3797
by: bnchs | last post by:
This is C code. I am trying to fill each node's Parent field with its parent because I am drawing nodes to the screen. However, I have not been able to get this working. Reading the output from the code and tracing it, it seems that the code does not continue recursing down into the tree to insert. Can someone please point me to what is wrong and help me understand why it is wrong? Thank you. struct AvlNode* Insert( gint X, struct AvlNode*...
0
8718
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9198
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9047
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7973
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5967
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4477
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4738
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3175
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2541
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.