Style question - defining immutable class data members

Aaron Brady castironpi at gmail.com
Mon Mar 16 05:31:59 EDT 2009


On Mar 15, 9:54 pm, "Rhodri James" <rho... at wildebst.demon.co.uk>
wrote:
> On Sun, 15 Mar 2009 23:26:04 -0000, Aaron Brady <castiro... at gmail.com>  
> wrote:
>
>
>
>
>
> > On Mar 15, 1:50 pm, "Rhodri James" <rho... at wildebst.demon.co.uk>
> > wrote:
> >> On Sun, 15 Mar 2009 17:55:25 -0000, Aaron Brady <castiro... at gmail.com>  
> >> wrote:
>
> >> > On Mar 15, 12:39 pm, John Posner <jjpos... at snet.net> wrote:
> >> >> (My apologies if the thread has already covered this.) I believe I  
> >> >> understand the WHAT in this situation, but I don't understand the >>  
> >> WHY  
> [snip]
> >> > Yes.  If you access an attribute, you want the search to go like this:
>
> >> > - check instance for attribute
> >> > - check class for attribute
> >> > - check base classes for attribute
>
> >> But do you, though?  The only occasion I can think of that I'd want
> >> the search to go past the instance is this "auto-initialisation",
> >> and frankly I'd rather do that in an __init__ anyway.  Perhaps
> >> static methods or class methods work that way, I don't know how
> >> the innards of the interpreter handle that.
>
> > As Bruno stated, yes, for resolving 'self.method', and other
> > descriptors.  You acknowledge that 'foo.jam' would need to identify
> > itself as coming from an instance, class, or base, since the only
> > entries in 'foo's dictionary are those attributes which are particular
> > to 'foo'.
>
> No, I don't acknowledge that.  (Note that John's original question
> was *WHY*, and you effectively gave him a *HOW* answer by asserting
> that it's what he wants.  I'm playing Devil's Advocate to an extent.)

Very valid.  Evidently I thought that the 'how' would justify it.
However, a *WHAT* in this case is pretty close to a 'why'.

You can access instance attributes and class attributes with the same
syntax.  It has the advantage that you can mix two statements into
one:

attr= x.attr
<====>
if 'attr' in x.__dict__:
    attr= x.__dict__[ 'attr' ]
else:
    attr= x.__class__.__dict__[ 'attr' ]

Otherwise, either /1, every instance has its own entries for class
functions, and subsequent changes to the class don't affect existing
instances, or /2, every method call is of the form x.__class__.foo
( ).  They're both bad.  (...unless that's a false dilemma.)

> "self.method" is not the same object as "Class.method"; one's bound
> and the other isn't, for starters.  It's therefore by no means
> obvious that method lookup isn't being done via the instance's
> dictionary.

No.  What makes it obvious is that subsequent changes to the class
affect existing instances.

> After all, some kind of binding has to be done at
> instance creation time.  If you're telling me that it's time-
> efficient (or at least space-efficient and not horribly time-
> inefficient) to use the class dictionary and magically know to
> wrapper the call, then we've that makes things different and
> gives us a reason for wanting that search order.

It's more space efficient.  However, the additional time involved in
the 'instance-first-class-second' search, plus the process of creating
the bound method (once per access, by the way, currently), can
counterweigh or outweigh that.

> It's the same story with descriptors; in fact they mask rather
> more of the detail of what they're doing and look at first glance
> more tightly bound to the instance than the class.  Further steps
> get you to the same "why" answer, but they are further steps.

No, I did leave some of the steps to the analysis tacit.  My mistake.

> > Class attributes are grouped together in the class dictionary,
> > instance attributes are grouped together in the instance dictionary,
> > and instances need to see both.
>
> True, but they don't need to see both with instance attribute
> syntax.  That they can is what we're trying to justify here.

As above, the alternatives (explicit class access and redundant
instance membership) are bad.

> > If you have a counter-proposal, either for a wishlist for behavior, or
> > a way of arranging its implementation, I for one would entertain it,
> > even on the c-l-python newsgroup, and even though it wouldn't have
> > much of a chance of making it in to Python.
>
> Nope, no proposal.  Mildly considering one, but I thought I'd try
> understanding why what happens is considered a good thing before I
> let my hare-brainedness off the leash.

I know... that was just a tactic to get you to do the work of
enumerating the alternatives.  Bah.

> > As a side note, this argument of 'whose methods am I seeing?' has an
> > inconvenient artifact: the behavior of 'foo.method'.  'foo.method'
> > needs to refer to an instance method, due to not needing the 'self'
> > attribute respecified; while 'foo.__class__.method' needs to refer to
> > a plain function.  Python's solution is a skeleton type, that just
> > redirects 'foo.method( )' to an append of the arguments plus call.
>
> As I said, this inconvenient artefact is exactly why I didn't think
> assuming instance method lookup happened via the *class* dictionary
> was safe!

It's not.  If you assign a function to an instance attribute, the
lookup doesn't extend to the class dictionary.

> >>  Actually what I want is for
> >> the attribute to be stored where I told it, which could well be in
> >> the class.  If the attribute is specified as "self.attribute", then
> >> yes, put it in the instance.

You can store in instances and classes, but you need different syntax
to specify which one you want.  You can use the same syntax to read
from them.  However, the syntax for accessing an instance member, and
forgoing the search to class and bases, is more complicated.  I guess
the designer just figured that that wouldn't be very common.

> > The way C++ works is by allocating storage for the data and method
> > pointers in a class.
>
> > class X {
> >     int foo;
> >     char jam[ 12 ];
> >     int foojam( int y, int z ) { ...; }
> > };
>
> > X requires 20 bytes: 4 for 'foo', 12 for 'jam', and 4 for 'foojam'...
> > under a 32-bit target.  (The 4 for 'foojam' come from a virtual
> > function table, for polymorphic objects.... er, technically.)
>
> I'm sorry, I'm utterly baffled.  Why is this relevant to my bit
> of pedantry?

Um, I don't remember exactly... even -with- my exact words in front of
me.  Probably, there's no fallback to the class to check for
attributes, but there is no dynamic addition to instances or classes.
It's an example of, "everything's in the instance".

>
> > You get what you want: attributes are stored where you tell them.  But
> > where are they read from?
>
> No, *why*.  These questions aren't about what the search order
> is, they're about why there is a search order.

Convenience and economy, mostly.

a) duck.quack( )
b) duck.__class__.quack( )
c) Duck.quack( duck )

'a' is the most convenient, and it's more common to want it to succeed
with that meaning than fail; and there's still a way to get it to fail
anyway.

The underlying principle involved is somewhat delicate to achieve:
make everything possible, and the common easy.

>
> --
> Rhodri James *-* Wildebeeste Herder to the Masses

P.S.  Do you pronounce 'wildebeeste' like 'vildebeeste'?



More information about the Python-list mailing list