Best practice for object attributes?

Carl Banks imbosol-1049309043 at aerojockey.com
Wed Apr 2 14:03:26 EST 2003


Michael Sparks wrote:
> laotseu wrote:
> 
>> One outstanding advantage of the second 'idiom' is that it's the right
>> way to define *instance* attributes !-) (which is probably what your
>> looking for)
> 
> Given the first idiom seems to create instance attributes by cloning the
> current state of the class attributes, I'm finding it useful is where
> certain instance attributes are unlikely to differ much between
> instances, or changes infrequently related to object creation. 

It doesn't.


> For example: if you're creating large numbers of objects which need a
> ballpark timestamp, you could timestamp the class periodically, and
> reduce your number of calls to gettimeofday.

Are you sure?  Look:

>>> class A:
...     time = 'today'
... 
>>> B = A()
>>> B.time
'today'
>>> A.time = 'tomorrow'
>>> C = A()
>>> C.time
'tomorrow'

So far so good.  But look at what happened to B:

>>> B.time
'tomorrow'



> Another example is in a custom logging class - it has default logging
> levels for most objects defined by the class attributes, but can be
> raised/lowered on a per instance basis, or globally for all new objects
> by changing the class attributes.
> 
>> With the first form you get *class* attributes.  Make 2 instances of
>> Shrubbery1, change the name of first one to 'Brian', print the name of
>> the second one, and then try to understand what is the purpose of the
>> 'self' argument in methods.
> 
> I'm aware of the meaning of self :) and know that the first form gives
> you class attributes. The reason I was asking is because I'd noticed
> the following interesting behaviour, which if _intentional_ is useful.
> 
>>>> a=shrubbery1()
>>>> b=shrubbery1()
>>>> a.name,b.name,shrubbery1.name
> (None, None, None)                  # a.name, b.name exist - presumed
>                                    # references to shrubbery1.name
>>>> a.name="hello"
>>>> a.name,b.name,shrubbery1.name
> ('hello', None, None)               # Bzzzt! a.name seems to be
>                                    # independent
>>>> b.name="brian"
>>>> a.name,b.name,shrubbery1.name
> ('hello', 'brian', None)            # Hmm. So is b.name
>>>> shrubbery1.name="Surprise"
>>>> a.name,b.name,shrubbery1.name
> ('hello', 'brian', 'Surprise')      # Changing class leaves existing
>                                    # instances unaffected.

Try changing the class *before* changing the instances.  You will see
that it does affect the instances.


>>>> a=shrubbery1()                  # Get 2 fresh copies.
>>>> b=shrubbery1()
>>>> a.name,b.name,shrubbery1.name
> ('Surprise', 'Surprise', 'Surprise') # a.name, b.name cloned from
>                                     # shrubbery1.name
> 
> As far as I can tell the behaviour seems to be that you automatically
> gain instance attributes that clone the state of the class attributes
> at object creation time, which are then independent and can be modified
> via the usual self.<attr> syntax later on. (They cannot however be
> modified via the self.__dict__[<attr>] syntax since they're not stored
> there)

You're missing an important point however: when you "modify" a class
attribute using self, what you're really doing it adding an item to
the instance dict.  Before you modified the attribute, there was no
such attribute in the instance dict



> For reference, this behaviour holds with the following python versions:
>   * python 2.3 alpha 2
>   * python 2.2.1
>   * python 2.2.2 stackless
> 
> Given it seems consistent across versions, and seems useful (changing
> default values for later objects just by updating a class attribute -
> eg an external timestamper perhaps) I was wondering if there's a good
> reason not to use this.


I highly recommend you initialize all instance variables in __init__,
and never use class variables as "default instance varibles."  It can
lead to all kinds of subtle bugs.  Even if you fully understood the
dynamics (and it seems from this post you don't), another person
working on the code might not.  It's also possible that you might want
to do something later that depends on instance variables being in the
instance's dict.  (Maybe you want to pickle it, and expect it to have
the same value when unpickled.)

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.


-- 
CARL BANKS




More information about the Python-list mailing list