Best practice for object attributes?

Jp Calderone exarkun at intarweb.us
Wed Apr 2 10:39:32 EST 2003


On Wed, Apr 02, 2003 at 04:12:36PM +0100, Michael Sparks wrote:
> [snip]
> 
> 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.
> >>> 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)
> 

  Not quite.  What really happens is simpler.  When an attribute lookup on
an instance fails, the lookup proceeds to the instance's class object (if it
fails there, it proceeds to the class object's bases, and so on).  The
attributes aren't cloned, and the time of creation matters not.  Here is an
example:

    class Foo:
        attr = 'String'

    f = Foo()
    Foo.attr = 10
    print f.attr


  You are still right that this behavior is useful. >:)  One important thing
to note, though, is that this is almost -never- what you want when the
attributes refer to mutable objects.  For example:

    class Foo:
        attr = []
        def update(self, value):
            self.attr.append('It is a value: %r' % (value,))

    f = Foo()
    g = Foo()
    g.update(f)
    print f.attr

  As you can see, f and g (and indeed Foo) are sharing the same list object. 
Sometimes this is what you want, but more frequently it is not, and your
only choice is to initialize the attribute to a new list object in some
instance method, usually __init__.

  Hope this helps,

  Jp

-- 
        "I quite agree with you," said the Duchess; "and the moral of
that is -- Be what you would seem to be' -- or, if you'd like it put
more simply -- Never imagine yourself not to be otherwise than what it
might appear to others that what you were or might have been was not 
otherwise than what you had been would have appeared to them to be
otherwise.'"       -- Lewis Carrol, "Alice in Wonderland"
-- 
 up 13 days, 12:00, 3 users, load average: 0.01, 0.02, 0.00





More information about the Python-list mailing list