Best practice for object attributes?

Michael Sparks michaels at rd.bbc.co.uk
Fri Apr 4 04:30:28 EST 2003


Carl, Jp Calderone, laotseu:

Thanks for the comments between the 3 of you I now understand the
behaviour of the following ....

> python2.3
Python 2.3a2 (#1, Feb 20 2003, 09:54:48)
[GCC 3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>1> class A: time='today'
...
>2> B=A()
>3> B_CB=A()
>4> B.time='yesterday'
>5> A.time='tomorrow'
>6> C=A()
>7> C.time
'tomorrow'          #1#
>8> B.time
'yesterday'         #2#
>9> B_CB.time
'tomorrow'          #3#

... to be this ....

1 Creates a class with class attribute "time", value 'today'.
2 Creates an object "B" with an empty __dict__, no local attributes
3 Creates an object "B_CB" with an empty __dict__, no local attributes
4 Inserts into "B" a local attribute "time" with value 'tomorrow'
5 Changes the class attribute "time" to 'tomorrow'
6 Creates an object "C" with an empty __dict__, no local attributes

7 The system looks in C for a local attribute time, and does not find
  one. It then looks in the class to see if it has attribute time, finds
  one and treats *that* as the value of C.time. 
  Hence the system gets the value of A.time - specifically 'tomorrow'
  hence the output on line #1#

8 The system looks in B for a local attribute time - it finds one with
  the value 'yesterday' in B's dict, hence the output on line #2#

9 The system looks in B_CB for a local attribute time, and does not find
  one. It then looks in the class to see if it has attribute time, finds
  one and treats *that* as the value of B_CB.time. 
  Hence the system gets the value of A.time - specifically 'tomorrow'
  hence the output on line #3#

This is based on these two explanations:

JP Calderone> When an attribute lookup on an instance fails, the lookup
JP Calderone> proceeds to the instance's class object (if it fails
JP Calderone> there, it proceeds to the class object's bases, and so
JP Calderone> on). 

Carl Banks> Before you modified the attribute, there was no
Carl Banks> such attribute in the instance dict

Carl Banks wrote:
> Even if you fully understood the dynamics (and it seems from this post
> you don't), another person working on the code might not.  

I didn't before I posted - I'd noticed the behaviour but wasn't aware
what was happening - value copying, copy on write, something different.
Turns out it's the something different - which *could* be termed
localisation on write, but that'd be almost as misleading as copying.
(since I presume you have all the shallow vs deep issues to consider)

For the system I'm writing this behaviour strikes me as a useful
optimisation of the system rather than necessary. The system will have
large numbers of objects all with a number of identical attributes -
which is why I considered placing them in the class rather than
instances. I accidentally discovered the syntax self.attribute to get
at the class attribute and was then rather surpised to be able to
modify the value on a per instance basis.

As a result if I hit speed or memory bottlenecks using this behaviour
would be useful, otherwise using the form of shrubbery2 - __init__
initialisation rather than class attributes is the better, less
surprising to others route to go down.

ie only use this shallow "localisation on write" if I hit speed/memory
problems, and if that happens document it's usage well.

> Generally, you should only use class variables when you want *all*
> (not most, all) instances to have the same attribute, and you want
> *all* instances to reflect any changes to it.

This is the case for the situation I was considering using the class
variables. Specifically I create large amounts of components with the
same externally defined interface defined in two attributes inboxes &
outboxes (which are then used by the superclass constructor to set up
more interesting instance attributes). 

Also each of these has access to a local debugger, again globally
defined. The thing I found suprising which prompted this question what
the fact that I was able to both globally change debugging levels -
which I wanted - and locally to an instance change debugging levels -
which was an unexpected bonus. However, I didn't understand where I
gained that behaviour and as a result didn't want to rely on it without
understanding it, especially if it's an unintentional side effect.

Upshot - I'll probably keep the components with the class attributes
they have - they really are the same for all instances of the class,
and have a long think about the debugger.

laotseu> oops ! I should have shut my mouth :(

Absolutely NOT - it made me explain myself better! Thanks :)

Thanks to all for your comments/advice - much appreciated!


Michael
-- 
Michael.Sparks at rd.bbc.co.uk    
British Broadcasting Corporation, Research and Development
Kingswood Warren, Surrey KT20 6NP

This message (and any attachments) may contain personal views
which are not the views of the BBC unless specifically stated.






More information about the Python-list mailing list