473,379 Members | 1,253 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,379 software developers and data experts.

Class Variable Access and Assignment

This has to do with class variables and instances variables.

Given the following:

<code>

class _class:
var = 0
#rest of the class

instance_b = _class()

_class.var=5

print instance_b.var # -> 5
print _class.var # -> 5

</code>

Initially this seems to make sense, note the difference between to last
two lines, one is refering to the class variable 'var' via the class
while the other refers to it via an instance.

However if one attempts the following:

<code>

instance_b.var = 1000 # -> _class.var = 5
_class.var = 9999 # -> _class.var = 9999

</code>

An obvious error occurs. When attempting to assign the class variable
via the instance it instead creates a new entry in that instance's
__dict__ and gives it the value. While this is allowed because of
pythons ability to dynamically add attributes to a instance however it
seems incorrect to have different behavior for different operations.

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.

Many thanks to elpargo and coke. elpargo assisted in fleshing out the
best way to present this.

perhaps this was intended, i was just wondering if anyone else had
noticed it, and if so what form would you consider to be 'proper'
either referring to class variables via the class itself or via
instances of that class. Any response would be greatly appreciated.
Graham

Nov 3 '05 #1
166 8447
On Thu, 03 Nov 2005 01:43:32 -0800, Graham wrote:

[snip]
print instance_b.var # -> 5
print _class.var # -> 5

</code>

Initially this seems to make sense, note the difference between to last
two lines, one is refering to the class variable 'var' via the class
while the other refers to it via an instance.
That's not correct. The line instance_b.var is referring to an instance
attribute. According to the usual Object Oriented model of inheritance, if
the instance does not have an attribute, the class is searched next.

So instance_b.var and _class.var are asking for two different things. The
first says, "Search the instance for attribute var, then the class." The
second says "Search the class."

BTW, a leading underscore is the convention for a private(ish) variable.
The convention for naming a variable after a reserved word is a trailing
underscore class_. In this case, there is also the convention that classes
should start with a capital, so you have Class and instance. (instance, of
course, is not a reserved word.)

However if one attempts the following:

<code>

instance_b.var = 1000 # -> _class.var = 5
_class.var = 9999 # -> _class.var = 9999

</code>

An obvious error occurs.
I see no error. No exception is raised when I try it: I get the expected
results. Assigning to an instance assigns to the instance, assigning to
the class assigns to the class. That's normal OO behaviour.

When attempting to assign the class variable
via the instance it instead creates a new entry in that instance's
__dict__ and gives it the value.
You might *want* to assign to the class attribute, but that's not what you
are doing. You are assigning to the instance.

Admittedly, it might not be the behaviour you expect, but it is the
standard behaviour in (as far as I know) all OO languages.

If you want to assign to the class attribute, you either assign to the
class directly, or use instance_b.__class__.var.

While this is allowed because of
pythons ability to dynamically add attributes to a instance however it
seems incorrect to have different behavior for different operations.
Surely you can't mean that? Why would you want different operations to
have the same behaviour?

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.


There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.
--
Steven.

Nov 3 '05 #2
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.


There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.


No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1

--
Antoon Pardon
Nov 3 '05 #3
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.


There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.


No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


I find it confusing at first, but I do understand what happens :-)

But really, what should be done different here?

S.
Nov 3 '05 #4
Antoon Pardon wrote:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.


There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.

No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1

I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 3 '05 #5
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
>> There are two possible fixes, either by prohibiting instance variables
>> with the same name as class variables, which would allow any reference
>> to an instance of the class assign/read the value of the variable. Or
>> to only allow class variables to be accessed via the class name itself.
>
> There is also a third fix: understand Python's OO model, especially
> inheritance, so that normal behaviour no longer surprises you.
No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


I find it confusing at first, but I do understand what happens :-)


I understand what happens too, that doesn't make it sane behaviour.
But really, what should be done different here?


I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.

In the line: b.a += 2, the b.a should be refering to the class variable
or the object variable but not both. So either it could raise an
attribute error or add two to the class variable.

Sure one could object to those sematics too, but IMO they are preferable
to what we have now.

--
Antoon Pardon
Nov 3 '05 #6
Steve Holden <st***@holdenweb.com> writes:
class A:
a = 1
b = A()
b.a += 2
print b.a
print A.a
Which results in
3
1

I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


class A:
a = []
b = A()
b.append(3)
print b.a
print a.a

Compare and contrast.
Nov 3 '05 #7
On Thu, 03 Nov 2005 11:55:06 +0000, Antoon Pardon wrote:
No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


Seems perfectly sane to me.

What would you expect to get if you wrote b.a = b.a + 2? Why do you expect
b.a += 2 to give a different result?

Since ints are immutable objects, you shouldn't expect the value of b.a
to be modified in place, and so there is an assignment to b.a, not A.a.

On the other hand, if this happened:

py> class A:
.... a = []
....
py> b = A()
py> b.a.append(None)
py> print b.a, A.a
[None], []

*then* you should be surprised.

(Note that this is not what happens: you get [None], [None] as expected.
The difference is that append modifies the mutable list in place.)

--
Steven.

Nov 3 '05 #8
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

....
No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1
I find it confusing at first, but I do understand what happens :-)


I understand what happens too, that doesn't make it sane behaviour.
But really, what should be done different here?


I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.


It doesn't.
In the line: b.a += 2, the b.a should be refering to the class variable
or the object variable but not both. So either it could raise an
attribute error or add two to the class variable.


It does exactly what you say. It adds 2 to the a *instance variable* of
the object instance in 'b'. It doesn't touch the *class variable* A.a
which is still 1.

S.
Nov 3 '05 #9
Paul Rubin wrote:
Steve Holden <st***@holdenweb.com> writes:
class A:
a = 1
b = A()
b.a += 2
print b.a
print A.a
Which results in
3
1


I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?

class A:
a = []
b = A()
b.append(3)
print b.a
print a.a

Compare and contrast.


append() guarantees to modify a mutable object in place. Augmented
assignment operations don't,but are "normally" equivalent to

name = name operator value

In the former case exactly such semantics are implemented. I still don;t
see anyone suggesting a better outcome for the augmented assignment.

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 3 '05 #10
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
>> No matter wat the OO model is, I don't think the following code
>> exhibits sane behaviour:
>>
>> class A:
>> a = 1
>>
>> b = A()
>> b.a += 2
>> print b.a
>> print A.a
>>
>> Which results in
>>
>> 3
>> 1
>
> I find it confusing at first, but I do understand what happens :-)
I understand what happens too, that doesn't make it sane behaviour.
> But really, what should be done different here?


I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.


It doesn't.


Yes it does. If the b.a refers to the instance variable, then an
AttributeError should be raised, because the instance variable doesn't
exist yet, so you can't add two to it.

If the b.a refers to the class variable then two should be added to it.

Neither happens instead we get some hybrid in which an instance varible
is created that gets the value of class variable incrented by two.
In the line: b.a += 2, the b.a should be refering to the class variable
or the object variable but not both. So either it could raise an
attribute error or add two to the class variable.


It does exactly what you say. It adds 2 to the a *instance variable* of
the object instance in 'b'.


There is no instance variable at that point. How can it add 2, to
something that doesn't exist at the moment.
It doesn't touch the *class variable* A.a which is still 1.


But it accesses the class variable.

--
Antoon Pardon
Nov 3 '05 #11
Op 2005-11-03, Steve Holden schreef <st***@holdenweb.com>:
Antoon Pardon wrote:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.

There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.

No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1

I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


No. I don't think a superior outcome is necessary to see that this is
not sane behaviour. I don't care that much on how it gets fixed.

--
Antoon Pardon
Nov 3 '05 #12
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:
On Thu, 03 Nov 2005 11:55:06 +0000, Antoon Pardon wrote:
No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1
Seems perfectly sane to me.

What would you expect to get if you wrote b.a = b.a + 2?


I would expect a result consistent with the fact that both times
b.a would refer to the same object.
Why do you expect
b.a += 2 to give a different result?
I didn't know I did.
Since ints are immutable objects, you shouldn't expect the value of b.a
to be modified in place, and so there is an assignment to b.a, not A.a.


You are now talking implementation details. I don't care about whatever
explanation you give in terms of implementation details. I don't think
it is sane that in a language multiple occurence of something like b.a
in the same line can refer to different objects

I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2

--
Antoon Pardon.
Nov 3 '05 #13
You see,
The seen behavior is due to the result of python's name
binding,scoping scheme.
Let me give you an example,
class A:
i=0
def t(self):
print self.i
self.i=4
then
a=A()
a.i is 0
a.t()
then,
A.i is 0
a.i is 4

In the function, it first searches for i in its local scope, on not
finding it, accesses the class object's i.
then the next line, is an assignment, which binds (creates a new
variable) in the instance's scope. you can use the buit-in id function
to verify it.

the same thing happens in the case of b.a = b.a + 2 .... search for b.a
not found, read the value from the enclosing scope (of the class
object).... then assign b.a to the local scope, with the value 3.

But, I think your question about the sanity of the behaviour should be
analysed sincerely....

if,
k=0
def f():
print k
k=k+1
raises UnboundLocalError, then how is it accepted in the former case?
hmmm....

maybe, my arguments are hapazard.... but, i'll get to know when i'm
flamed ;)

Nov 3 '05 #14
Antoon Pardon enlightened us with:
I would expect a result consistent with the fact that both times b.a
would refer to the same object.


"b.a" is just a name, not a pointer to a spot in memory. Getting the
value associated with that name is something different from assigning
a new value to that name.

Sybren
--
The problem with the world is stupidity. Not saying there should be a
capital punishment for stupidity, but why don't we just take the
safety labels off of everything and let the problem solve itself?
Frank Zappa
Nov 3 '05 #15
Op 2005-11-03, venk schreef <ve****************@gmail.com>:
You see,
The seen behavior is due to the result of python's name
binding,scoping scheme.
I know what causes the behaviour. But I still think it is
not sane behaviour.

...

the same thing happens in the case of b.a = b.a + 2 .... search for b.a
not found, read the value from the enclosing scope (of the class
object).... then assign b.a to the local scope, with the value 3.


This is an explanation depending on a specific implementation.

Now can you give me a language design argument that supports the
idea that in "b.a = b.a + 2" b.a refers to two different objects.

And even if you could do that, can you give such an argument that
in "b.a += 2" that one occurence of b.a should refer to two different
objects.

Suppose I have code like this:

for i in xrange(1,11):
b.a = b.a + i

Now the b.a on the right hand side refers to A.a the first time through
the loop but not the next times. I don't think it is sane that which
object is refered to depends on how many times you already went through
the loop.

--
Antoon Pardon
Nov 3 '05 #16
Op 2005-11-03, Sybren Stuvel schreef <sy*******@YOURthirdtower.com.imagination>:
Antoon Pardon enlightened us with:
I would expect a result consistent with the fact that both times b.a
would refer to the same object.


"b.a" is just a name, not a pointer to a spot in memory. Getting the
value associated with that name is something different from assigning
a new value to that name.


If that was all to it, one would expect the following to work to:
a = 1
def f(): .... a += 2
.... f()


Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment

But I'll word it differently:

I would expect a result consistent with the fact that both times
b.a would be resolved in the same name space.

Nov 3 '05 #17
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, venk schreef <ve****************@gmail.com>:
You see,
The seen behavior is due to the result of python's name
binding,scoping scheme.


I know what causes the behaviour. But I still think it is
not sane behaviour.

...

the same thing happens in the case of b.a = b.a + 2 .... search for b.a
not found, read the value from the enclosing scope (of the class
object).... then assign b.a to the local scope, with the value 3.


This is an explanation depending on a specific implementation.

Now can you give me a language design argument that supports the
idea that in "b.a = b.a + 2" b.a refers to two different objects.

And even if you could do that, can you give such an argument that
in "b.a += 2" that one occurence of b.a should refer to two different
objects.


Your problem is a namespace conflict together with a certain
stubborness about lookup order :-)

It is really simple. When you say b.a then the instance variable 'a'
is looked up first. If it does not exist then a class variable lookup
is done.

Remember, Python is a dynamic language.

It is all according to how things have been in Python for a long time.

The real issue here is that you should propery name class variables so
that there can't be any confusion about class or instance scope. I use
all uppercase identifiers for class variables for example.

S.
Nov 3 '05 #18
hey,
did u read my reply fully? i too feel that this matter of raising
unbound local error in one case and not raising it in the other must be
analysed...

quoting from the documentation
"If a name binding operation occurs anywhere within a code block, all
uses of the name within the block are treated as references to the
current block. This can lead to errors when a name is used within a
block before it is bound. This rule is subtle. Python lacks
declarations and allows name binding operations to occur anywhere
within a code block. The local variables of a code block can be
determined by scanning the entire text of the block for name binding
operations."

Nov 3 '05 #19
Stefan Arentz wrote:
It is really simple. When you say b.a then the instance variable 'a'
is looked up first. If it does not exist then a class variable lookup
is done.


This mixing of class and instance variable might be the cause of
confusion...

I think of it as follows:
1 When the class statement ends a class object is created which is
filled by all the statements inside the class statement
This means all variables and functions (methods) are created according
to the description.
NOTE This happens just once.
2 When an instance of the class is created, what effectively happens is
that a shallow copy of the class object is made.
Simple values and object references are copied.

This explains:
- why methods and complex objects (e.g. lists) are shared among
instances of a class and the class itself
- simple values are not shared

--eric

Nov 3 '05 #20
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, venk schreef <ve****************@gmail.com>:
> You see,
> The seen behavior is due to the result of python's name
> binding,scoping scheme.
I know what causes the behaviour. But I still think it is
not sane behaviour.

> ...
>
> the same thing happens in the case of b.a = b.a + 2 .... search for b.a
> not found, read the value from the enclosing scope (of the class
> object).... then assign b.a to the local scope, with the value 3.


This is an explanation depending on a specific implementation.

Now can you give me a language design argument that supports the
idea that in "b.a = b.a + 2" b.a refers to two different objects.

And even if you could do that, can you give such an argument that
in "b.a += 2" that one occurence of b.a should refer to two different
objects.


Your problem is a namespace conflict together with a certain
stubborness about lookup order :-)

It is really simple. When you say b.a then the instance variable 'a'
is looked up first. If it does not exist then a class variable lookup
is done.


Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?
Remember, Python is a dynamic language.
So? Python being a dynamic language doesn't prevent the following to fail:
a=1
def f(): .... a += 2
.... f()

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment
It is all according to how things have been in Python for a long time.
Unsane behaviour for a long time is still unsane behaviour.
The real issue here is that you should propery name class variables so
that there can't be any confusion about class or instance scope. I use
all uppercase identifiers for class variables for example.


The fact that this can be regarded as unwise coding, doesn't imply
it is sane behaviour of python. Variable shadowing happens. I don't
consider it sane behaviour if the same reference in a line gets
resolved in different name spaces

--
Antoon Pardon
Nov 3 '05 #21
Again (blink) quoting from the docs "
For targets which are attribute references, the initial value is
retrieved with a getattr() and the result is assigned with a setattr().
Notice that the two methods do not necessarily refer to the same
variable. When getattr() refers to a class variable, setattr() still
writes to an instance variable. For example:

class A:
x = 3 # class variable
a = A()
a.x += 1 # writes a.x as 4 leaving A.x as 3
"

I felt a wee bit clear after going thru the doc... attribute
referencing is not the same as searching the variable in the enclosing
scopes.... But, i still feel the inconsistency...

Nov 3 '05 #22
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

....
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?
Because it really is executed as:

b.a = b.a + 2

1. get 't'b.a and store it in a temporary 't' (found the instance)
2. add 2 to 't'
3. store 't' in 'b.a'

The last operation stores it into an instance variable.
Remember, Python is a dynamic language.


So? Python being a dynamic language doesn't prevent the following to fail:
a=1
def f(): ... a += 2
... f()

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment


See the 'global' keyword.

s.
Nov 3 '05 #23
Stefan Arentz <st***********@gmail.com> writes:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?


Because it really is executed as:

b.a = b.a + 2

1. get 't'b.a and store it in a temporary 't' (found the instance)


Oops.

1. get b.a and store it in a temporary 't' (found the class variable 'a')

S.

Nov 3 '05 #24
Op 2005-11-03, venk schreef <ve****************@gmail.com>:
hey,
did u read my reply fully? i too feel that this matter of raising
unbound local error in one case and not raising it in the other must be
analysed...
Yes, it seems I didn't respond to your satisfaction, but since you
don't provide details I can't clarify.
quoting from the documentation
"If a name binding operation occurs anywhere within a code block, all
uses of the name within the block are treated as references to the
current block. This can lead to errors when a name is used within a
block before it is bound. This rule is subtle. Python lacks
declarations and allows name binding operations to occur anywhere
within a code block. The local variables of a code block can be
determined by scanning the entire text of the block for name binding
operations."


Well I wonder. Would the following code be considered a name binding
operation:

b.a = 5

--
Antoon Pardon
Nov 3 '05 #25
On Thu, 03 Nov 2005 04:30:09 -0800, Paul Rubin wrote:
Steve Holden <st***@holdenweb.com> writes:
> class A:
> a = 1
> b = A()
> b.a += 2
> print b.a
> print A.a
> Which results in
> 3
> 1
>

I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


class A:
a = []
b = A()
b.append(3)
print b.a
print a.a

Compare and contrast.

I take it then that you believe that ints like 1 should be mutable like
lists? Because that is what the suggested behaviour implies.

Ah, what a grand thing that would be! We could say:

0 += 1
1 += 2
7 -= 1

and then have 0 + 1 == 7. Think of the obfuscated code we could write!

--
Steven.

Nov 3 '05 #26
Op 2005-11-03, venk schreef <ve****************@gmail.com>:
Again (blink) quoting from the docs "
For targets which are attribute references, the initial value is
retrieved with a getattr() and the result is assigned with a setattr().
Notice that the two methods do not necessarily refer to the same
variable. When getattr() refers to a class variable, setattr() still
writes to an instance variable. For example:

class A:
x = 3 # class variable
a = A()
a.x += 1 # writes a.x as 4 leaving A.x as 3
"

I felt a wee bit clear after going thru the doc... attribute
referencing is not the same as searching the variable in the enclosing
scopes.... But, i still feel the inconsistency...


The documentation is IMO no help in arguing whether the behaviour is
sane or not. Unsane documented behaviour is still unsane.

So yes the documentation explains why such a result can happen,
but that doesn't change the fact that in this case we have
only one a.x on that line and the processing behind the scenes
refers to an x in two different name spaces.

I think that is unsane behaviour and giving an explanation on
why this particular behaviour arises from the current implemenation
doesn't change that.

--
Antoon Pardon
Nov 3 '05 #27
On Thu, 03 Nov 2005 12:50:51 +0000, Antoon Pardon wrote:
I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.
It doesn't.


Yes it does. If the b.a refers to the instance variable, then an
AttributeError should be raised, because the instance variable doesn't
exist yet, so you can't add two to it.


Then you don't approve of inheritance? That's fine, it is your choice, but
as far as I know, all OO languages include inheritance. I can't imagine
why you would want this to happen:

py> class BetterList(list):
.... def wobble(self):
.... """Wobble a list."""
.... pass
....
py> L = BetterList((1, 2, 3))
py> L.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'BetterList' object has no attribute 'sort'

(It just goes to show that Explicit is better than Implicit is not
*always* true.)
If the b.a refers to the class variable then two should be added to it.

Neither happens instead we get some hybrid in which an instance varible
is created that gets the value of class variable incrented by two.


Which is precisely the expected behaviour. First you fetch the instance
attribute, which by the rules of inheritance falls back to the value
of the class attribute if it doesn't yet exist (which, in this specific
case, it does not). Then you add two to it, and store the result in the
instance attribute. You can't increment the object 1 because it is
immutable.
In the line: b.a += 2, the b.a should be refering to the class
variable or the object variable but not both. So either it could raise
an attribute error or add two to the class variable.


It does exactly what you say. It adds 2 to the a *instance variable* of
the object instance in 'b'.


There is no instance variable at that point. How can it add 2, to
something that doesn't exist at the moment.


By the standard rules of inheritance.
--
Steven.

Nov 3 '05 #28
On Thu, 03 Nov 2005 12:53:37 +0000, Antoon Pardon wrote:
I don't suppose you'd care to enlighten us on what you'd regard as the
superior outcome?


No. I don't think a superior outcome is necessary to see that this is
not sane behaviour. I don't care that much on how it gets fixed.


It isn't broken, there is nothing to fix. The code does precisely what the
inheritance model promises to do.
--
Steven.

Nov 3 '05 #29
Antoon Pardon wrote:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:

There are two possible fixes, either by prohibiting instance variables
with the same name as class variables, which would allow any reference
to an instance of the class assign/read the value of the variable. Or
to only allow class variables to be accessed via the class name itself.


There is also a third fix: understand Python's OO model, especially
inheritance, so that normal behaviour no longer surprises you.

No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


On the other hand:
class C: .... a = [1]
.... b=C()
b.a += [2]
b.a [1, 2] C.a

[1, 2]

I can understand that Guido was a bit reluctant to introduce
+= etc into Python, and it's important to understand that they
typically behave differently for immutable and mutable objects.
Nov 3 '05 #30
Antoon Pardon wrote:
There is no instance variable at that point. How can it add 2, to
something that doesn't exist at the moment.


Because 'a += 1' is only a shorthand for 'a = a + 1' if a is an
immutable object? Anyway, the behaviour is well documented.

http://docs.python.org/ref/augassign.html says:

An augmented assignment expression like x += 1 can be rewritten as x = x
+ 1 to achieve a similar, but not exactly equal effect. In the augmented
version, x is only evaluated once. Also, when possible, the actual
operation is performed in-place, meaning that rather than creating a new
object and assigning that to the target, the old object is modified instead.

....

For targets which are attribute references, the initial value is
retrieved with a getattr() and the result is assigned with a setattr().
Notice that the two methods do not necessarily refer to the same
variable. When getattr() refers to a class variable, setattr() still
writes to an instance variable. For example:

class A:
x = 3 # class variable
a = A()
a.x += 1 # writes a.x as 4 leaving A.x as 3
Nov 3 '05 #31
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?
Because it really is executed as:

b.a = b.a + 2


That is an explanation, not a reason.
1. get 't'b.a and store it in a temporary 't' (found the instance)
2. add 2 to 't'
3. store 't' in 'b.a'

The last operation stores it into an instance variable.


[ I think you mean '(found the class variable)' in line 1 ]

All you are doing here is explain how the current implemantation treats
this. You are not giving arguments for why the result of this
implementation should be considered sane behaviour.
> Remember, Python is a dynamic language.


So? Python being a dynamic language doesn't prevent the following to fail:
>>> a=1
>>> def f():

... a += 2
...
>>> f()

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment


See the 'global' keyword.


You missed the point. If python being a dynamic language would be the
answer to the interaction between instance and class variable, why
then is the interaction between local and global variable different,
why wouldn't the f code be executed as follows

1. get a and store in in a temporary 't' (found the global)
2. add 2 to 't'
3. store 't' in 'a'

The last operation storing it in f's local namespace.

--
Antoon Pardon
Nov 3 '05 #32
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?


Because it really is executed as:

b.a = b.a + 2


That is an explanation, not a reason.


I'm just following the
1. get 't'b.a and store it in a temporary 't' (found the instance)
2. add 2 to 't'
3. store 't' in 'b.a'

The last operation stores it into an instance variable.


[ I think you mean '(found the class variable)' in line 1 ]

All you are doing here is explain how the current implemantation treats
this. You are not giving arguments for why the result of this
implementation should be considered sane behaviour.


Ah yes. Well, good luck with that. You seem to have decided that it is not
sane and who am I to argue with that. It depends on your state of mind :-)

The model makes sense in my opinion. If you don't like it then there are
plenty of other languages to choose from that have decided to implement
things differently.

Have fun, take care!

S.
Nov 3 '05 #33
On Thu, 03 Nov 2005 13:01:40 +0000, Antoon Pardon wrote:
Seems perfectly sane to me.

What would you expect to get if you wrote b.a = b.a + 2?
I would expect a result consistent with the fact that both times
b.a would refer to the same object.


class RedList(list):
colour = "red"

L = RedList(())

What behaviour would you expect from len(L), given that L doesn't have a
__len__ attribute?

Why do you expect
b.a += 2 to give a different result?


I didn't know I did.


It seems to me that you do.

You didn't appear to be objecting to a line like x = b.a assigning the
value of 1 to x (although perhaps you do). If that was the case, then it
is perfectly reasonable to expect b.a = x + 2 to store 3 into b.a, while
leaving b.__class__.a untouched.

Of course, if you object to inheritance, then you will object to x = b.a
as well.

Since ints are immutable objects, you shouldn't expect the value of b.a
to be modified in place, and so there is an assignment to b.a, not A.a.


You are now talking implementation details. I don't care about whatever
explanation you give in terms of implementation details. I don't think
it is sane that in a language multiple occurence of something like b.a
in the same line can refer to different objects


That's an implementation detail only in the sense that "while condition"
is a loop is an implementation detail. It is a *design* detail.

b is a name, and any reference to b (in the same namespace) will refer
to the same object. At least until you rebind it to another object.

But b.a is not a name, it is an attribute lookup, and by Python's rules of
inheritance that lookup will look up attributes in the instance, the
class, and finally any superclasses.

If you persist in thinking of b.a as a name referring to a single object,
of course you will be confused by the behaviour. But that's not what
attribute lookup does.

On the right hand side of an assignment, it will return the first existing
of b.__dict__['a'] or b.__class__.__dict__['a']. On the left hand of an
assignment, it will store into b.__dict__['a'].

I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2


Then it seems to me you have some serious design problems. Which would you
prefer to happen?
# Scenario 1
# imaginary pseudo-Python code with no inheritance:
class Paragraph:
ls = '\n' # line separator

para = Paragraph()
para.ls

=> AttributeError - instance has no attribute 'ls'

# Scenario 2
# imaginary pseudo-Python code with special inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r' # magically assigns to the class attribute
linux_para.ls

=> prints '\n\r'
# Scenario 3
# Python code with standard inheritance:
class Paragraph:
ls = '\n' # line separator

linux_para = Paragraph()
windows_para = Paragraph()
windows_para.ls = '\n\r'
linux_para.ls

=> prints '\n'

--
Steven.

Nov 3 '05 #34
On Thu, 03 Nov 2005 14:27:48 +0100, Sybren Stuvel wrote:
Antoon Pardon enlightened us with:
I would expect a result consistent with the fact that both times b.a
would refer to the same object.


"b.a" is just a name, not a pointer to a spot in memory. Getting the
value associated with that name is something different from assigning
a new value to that name.


You've got the right concept, but not the right terminology. The "b"
before the dot is a name. The "a" after the dot is a name. But the whole
thing together is not a name: b.a is an attribute reference. You wouldn't
call b["a"] a name, and you shouldn't call b.a a name either.
--
Steven.

Nov 3 '05 #35
On Thu, 03 Nov 2005 13:35:35 +0000, Antoon Pardon wrote:
Suppose I have code like this:

for i in xrange(1,11):
b.a = b.a + i

Now the b.a on the right hand side refers to A.a the first time through
the loop but not the next times. I don't think it is sane that which
object is refered to depends on how many times you already went through
the loop.


Well, then you must think this code is *completely* insane too:

py> x = 0
py> for i in range(1, 5):
.... x += i
.... print id(x)
....
140838200
140840184
140843160
140847128

Look at that: the object which is referred to depends on how many times
you've already been through the loop. How nuts is that?

I guess that brings us back to making ints mutable. I can't wait until I
can write 1 - 0 = 99 and still be correct!
--
Steven.

Nov 3 '05 #36
On Thu, 03 Nov 2005 14:13:13 +0000, Antoon Pardon wrote:
Fine, we have the code:

b.a += 2

We found the class variable, because there is no instance variable,
then why is the class variable not incremented by two now?


Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =
<something> to correspond to b.__class__.a = <something>?

I'm not saying that it couldn't, if that was the model for inheritance you
decided to use. I'm asking why would you want it? What is your usage case
that demonstrates that your preferred inheritance model is useful?
--
Steven.

Nov 3 '05 #37
On Thu, 03 Nov 2005 15:13:52 +0100, Eric Nieuwland wrote:
Stefan Arentz wrote:
It is really simple. When you say b.a then the instance variable 'a'
is looked up first. If it does not exist then a class variable lookup
is done.
This mixing of class and instance variable might be the cause of
confusion...

I think of it as follows:
1 When the class statement ends a class object is created which is
filled by all the statements inside the class statement
This means all variables and functions (methods) are created according
to the description.
NOTE This happens just once.


Yes.
2 When an instance of the class is created, what effectively happens is
that a shallow copy of the class object is made.
Simple values and object references are copied.
No.

py> class Parrot:
.... var = 0
....
py> p = Parrot()
py> Parrot.var is p.var
True
py> Parrot.var = {"Hello world": [0, 1, 2]}
py> Parrot.var is p.var
True

It all boils down to inheritance. When Python does a look up of an
attribute, it looks for an instance attribute first (effectively trying
instance.__dict__['name']). If that fails, it looks up the class second
with instance.__class__.__dict__['name'], and if that fails it goes into a
more complex search path looking up any superclasses (if any).

This explains:
- why methods and complex objects (e.g. lists) are shared among
instances of a class and the class itself
- simple values are not shared


No. it is all about the inheritance, and mutable/immutable objects.

--
Steven.

Nov 3 '05 #38
Steven D'Aprano wrote:
On Thu, 03 Nov 2005 15:13:52 +0100, Eric Nieuwland wrote:
2 When an instance of the class is created, what effectively happens
is
that a shallow copy of the class object is made.
Simple values and object references are copied.


No.

py> class Parrot:
... var = 0
...
py> p = Parrot()
py> Parrot.var is p.var
True
py> Parrot.var = {"Hello world": [0, 1, 2]}
py> Parrot.var is p.var
True

It all boils down to inheritance. When Python does a look up of an
attribute, it looks for an instance attribute first (effectively trying
instance.__dict__['name']). If that fails, it looks up the class second
with instance.__class__.__dict__['name'], and if that fails it goes
into a
more complex search path looking up any superclasses (if any).


Note my use of "effectively" and "shallow copy". Your example
demonstrates how Python postpones the shallow copy until you tell the
object to differ from the class/

The examples used only use a class an an instance thereof. They are
valid without inheritance.
This explains:
- why methods and complex objects (e.g. lists) are shared among
instances of a class and the class itself
- simple values are not shared


No. it is all about the inheritance, and mutable/immutable objects.


NO. You're referring to the implemented mechanism. Other
implementations with the same semantics are possible.

Nov 3 '05 #39
In article <dk**********@wake.carmen.se>,
Magnus Lycka <ly***@carmen.se> wrote:
....
On the other hand:
>>> class C: ... a = [1]
... >>> b=C()
>>> b.a += [2]
>>> b.a [1, 2] >>> C.a

[1, 2]

I can understand that Guido was a bit reluctant to introduce
+= etc into Python, and it's important to understand that they
typically behave differently for immutable and mutable objects.


As far as I know, Guido has never added a feature reluctantly.
He can take full responsibility for this misguided wart.

Donn Cave, do**@u.washington.edu
Nov 3 '05 #40
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
Then you don't approve of inheritance? That's fine, it is your choice, but
as far as I know, all OO languages include inheritance.


Some OO languages only implement inheritance for method calls. Class
variables don't get inherited.
Nov 3 '05 #41
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
What would you expect to get if you wrote b.a = b.a + 2? I would expect a result consistent with the fact that both times
b.a would refer to the same object.


Except they *don't*. This happens in any language that resolves
references at run time. Changing that would be changing a fundamental
- and *important* - feature of Python. Arbitrary restrictions to
prevent a single case of this from doing something people who aren't
used to suvh behavior are kludges, and would constitute a wart on the
language, pure and simple.

If you think this is bad, you should consider Jensen's device. It uses
call-by-name, which Python doesn't have. This defers evauation of an
argument until the actual use of the argument. You can fake it in
python by passing strings that you eval and/or lambdas that you call -
I'm not going to work out the details.

Here's the function:

real procedure SIGMA(x, i, n);
value n;
real x; integer i, n;
begin
real s;
s := 0;
for i := 1 step 1 until n do
s := s + x;
SIGMA := s;
end

The object referred to by "x" in the body of the loop is different
*every pass through the loop*, at least in the expected usage. That
usage is SIGMA(a(i), i, 10). Call-by-name evaluates a(i) when x is
mentioned. Since i changes with each pass through the loop, a(i) is a
different element in the array. So SIGMA(a(i), i, 10) is sum(a[:10])
(give or take). But SIGMA(a(i) * b(i), i, 10) is sum([a(i) * b(i) for
i in xrange(10)]) (more or less).

This isn't insane, it's a feature. It's a *very powerful*
feature. Yes, it causes behavior that's unexpected and appears to be
wrong to people who aren't used to resolving names at run time. They
need to get used to it. Then they can start taking advantage of it.
I think it even less sane, if the same occurce of b.a refers to two
different objects, like in b.a += 2


That's a wart in +=, nothing less. The fix to that is to remove +=
from the language, but it's a bit late for that.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Nov 3 '05 #42
Thanks to all of you for your reply's i had no idea it would get this
sort of response,
i've read through most of the posts and it seems that its a active
topic.

My question remains however, i suppose i'm not familiar with how this
functions in
other languages, but what is the common way of referring to a class
variable.

is <class>.<var> the norm?
or <instance>.<var> the norm.

I just seems to me that <instance>.<var> shouldn't defer to the class
variable if
an instance variable of the same name does not exists, it should, at
least how i
understand it raise an exception.

Is my thinking way off here?

Graham

Nov 3 '05 #43
"Graham" <gr***********@gmail.com> writes:
I just seems to me that <instance>.<var> shouldn't defer to the class
variable if
an instance variable of the same name does not exists, it should, at
least how i
understand it raise an exception.

Is my thinking way off here?


Yes. This behavior is how you get inheritted access to class
variables. Consider:

class Counter(object):
"A mutable counter."
# implementation elided

class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()

class B(A):
instance_count = Counter()

This is sufficient to count instances of A and B. If
self.instance_count raised an exception, you'd have to do something
like A.instance_count in A.__init__, which would mean the __init__ B
inherited from A would do the wrong thing. Currently, you can either
reference class variables by explicit class, or via inheritance, and
both behaviors are desirable. If you're going to disallow
self.class_variable, you need to come up with a mechanism to replace
the latter behavior.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Nov 3 '05 #44
On Thu, 03 Nov 2005 10:01:04 -0800, Paul Rubin wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
Then you don't approve of inheritance? That's fine, it is your choice, but
as far as I know, all OO languages include inheritance.


Some OO languages only implement inheritance for method calls. Class
variables don't get inherited.


Fascinating. Which languages, and what is the reasoning behind that?

I bet they are languages that force the class designer to spend half their
time writing setters and getters. Am I right?
--
Steven.

Nov 3 '05 #45
Many thanks your explaination cleared up many of the questions I had.
I know think i can understand the purpose, regardless of my opinion, i
do however think that one should be able to assign the value in the
same way it is accessed.
Given your previous example:
class Counter(object):
"A mutable counter."
# implementation elided class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()

if you changed class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()
to
class A(object):
instance_count = 0
def __init__(self):
self.instance_count = self.instance_count + 1


It would not work as planned. I understand all the reasons why this
occurs, but i dont understand why its implemented this way. Because it
acts in a different way than you expect. It seems to me that
self.instance_count should not create a new entry in the __dict__ if a
class variable of that name is already present anywhere in that objects
hierarchy.

Does that make sense?

Again thank you for explaination.

graham

Nov 4 '05 #46
On 3 Nov 2005 12:20:35 GMT, Antoon Pardon <ap*****@forel.vub.ac.be> wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:
Op 2005-11-03, Steven D'Aprano schreef <st***@REMOVETHIScyber.com.au>:

>> There are two possible fixes, either by prohibiting instance variables
>> with the same name as class variables, which would allow any reference
>> to an instance of the class assign/read the value of the variable. Or
>> to only allow class variables to be accessed via the class name itself.
>
> There is also a third fix: understand Python's OO model, especially
> inheritance, so that normal behaviour no longer surprises you.

No matter wat the OO model is, I don't think the following code
exhibits sane behaviour:

class A:
a = 1

b = A()
b.a += 2
print b.a
print A.a

Which results in

3
1


I find it confusing at first, but I do understand what happens :-)


I understand what happens too, that doesn't make it sane behaviour.
But really, what should be done different here?


I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.

In the line: b.a += 2, the b.a should be refering to the class variable
or the object variable but not both. So either it could raise an
attribute error or add two to the class variable.

Sure one could object to those sematics too, but IMO they are preferable
to what we have now.

A somewhat similar name space problem, where you could argue
that "a" prior to += should be seen as defined in the outer scope,
but lookahead determines that a is local to inner, period, so that
is the reference that is used (and fails).
def outer(): ... a = 1
... def inner():
... a += 2
... print a
... print 'outer a', a
... inner()
... print 'outer a', a
... outer()

outer a 1
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 7, in outer
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'a' referenced before assignment

Regards,
Bengt Richter
Nov 4 '05 #47
Graham wrote:
Many thanks your explaination cleared up many of the questions I had.
I know think i can understand the purpose, regardless of my opinion, i
do however think that one should be able to assign the value in the
same way it is accessed.
You mean, like this?

# access the value of an instance attribute:
x = instance.name

# assign the value of an instance attribute:
x = instance.name

No, of course that's not what you *meant* -- but it is
what you said. So what do you actually mean? I'll
assume you mean something like "the scoping rules for
access and assignment should be the same".

The problem is, they are. When you access an attribute,
Python looks in the instance scope, and if that lookup
fails, it looks in the class scope. When you assign an
attribute, at least ignoring the complication of slots,
Python uses the same scoping rules: assign to the
instance scope, WHICH ALWAYS SUCCEEDS, and if it fails,
assign to the class scope.

In fact, Python may not even bother to include code to
push the assignment to the class, since the assignment
at the instance level is guaranteed to either succeed,
or fail in such a way that you have no choice but to
raise an exception. But conceptually, assignment and
access are using the same scoping rules.

(Again, I stress that new-style classes with slots may
do things differently.)

Now that I've demonstrated that what you want is not
either "assignment and access should be the same", nor
"the scoping rules should be the same", can you
describe precisely what you do want?
See below for further details.

Given your previous example:
class Counter(object):
"A mutable counter."
# implementation elided
class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()


if you changed
class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()

to

class A(object):
instance_count = 0
def __init__(self):
self.instance_count = self.instance_count + 1

It would not work as planned.


Why not?

For starters, what is the plan? Do you want all
instances of class A to share state? Are all instances
of Counter supposed to share state?

Depending on whether you want the answers of those to
be Yes or No, you would pick one technique or the
other. Sometimes you want to increment mutables in
place, and sometimes you don't.

But I would suggest very strongly that in general, you
usually don't want instances to share state.

I understand all the reasons why this
occurs, but i dont understand why its implemented this way. Because it
acts in a different way than you expect. It seems to me that
self.instance_count should not create a new entry in the __dict__ if a
class variable of that name is already present anywhere in that objects
hierarchy.


But that would stop inheritance from working the
expected way.

In standard OO programming, you expect instances to
inherit behaviour from their class (and superclasses)
unless over-ridden. This lets you do something like this:

class Paper:
size = A4

Now all instances of Paper are created with a default
size of A4 -- they inherit that size from the class.

If you are localising your application for the US
market, you simply change the class attribute:

Paper.size = USLetter

and all the instances that inherit from the class will
now reflect the new default.

Now suppose you have a specific instance that needs a
different paper size:

instance = Paper()
instance.size = Foolscap

What do you expect should happen? Should all Paper
instances suddenly be foolscap size, or just the one?
If you say "just the one", then you want the current
behaviour. If you say "all of them", then you want
shared state -- but do you really want all class
instances, all the time, to have shared state?
You can get ride of that behaviour by getting rid of
inheritance, or at least inheritance of non-method
attributes. Then you have to write code like this:

class PrintableThing:
"""Prints a Thing object with prefix and suffix.
Customize the prefix and suffix by setting the
appropriate instance attributes.
"""

prefix = "START "
suffix = " STOP"

def __str__(self):
try:
# access the instance attributes,
# if they exist
prefix = self.prefix
suffix = self.suffix
except AttributeError:
# fall back to class attributes
prefix = self.__class__.prefix
suffix = self.__class__.suffix
# have you spotted the subtle bug in this code?
return prefix + self.thing + suffix

instead of:

class PrintableThing:
def __str__(self):
return self.prefix + self.thing + self.suffix
Even worse would be the suggestion that Python allowed
accessing instance.attribute to refer to either a class
or instance attribute, decided at runtime as it does
now, but *remembered* which it was so that assignment
went back to the same object.

That would mean that class attributes would mask
instance attributes -- or vice versa, depending on
which was created first. I assume that in general,
class attributes would be created before instances.

If you had a class with a default attribute, like
Paper.size above, you couldn't over-write it at the
instance level because instance.size would always be
masked by class.size. You would need to write code like
this:

class Paper:
default_size = A4

def __init__(self, size=None):
self.size = size

def print(self):
if self.size is None:
papersize = self.__class__.default_size
else:
papersize = self.size
do_something(papersize)
The standard inheritance model used by Python and all
OO languages I know of is, in my opinion, the optimal
model. It gives you the most convenient behaviour for
the majority of cases, and in those few cases where you
want non-standard behaviour (e.g. shared state) it is
easy to do with some variant of self.__class__.attribute.
--
Steven.

Nov 4 '05 #48
"Graham" <gr***********@gmail.com> writes:
Many thanks your explaination cleared up many of the questions I had.
I know think i can understand the purpose, regardless of my opinion, i
do however think that one should be able to assign the value in the
same way it is accessed.
That's not true in lots of cases, and would be a serious restriction
on the language. The rules for where a binding takes place are the way
the are for good reason. Restricting lookups to those rules would make
a number of things more difficult, and would make some features
(cough-closures-cough) nearly useless.
Given your previous example:
class Counter(object):
"A mutable counter."
# implementation elided
class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()

if you changed
class A(object):
instance_count = Counter()
def __init__(self):
self.instance_count.increment()


to
class A(object):
instance_count = 0
def __init__(self):
self.instance_count = self.instance_count + 1


It would not work as planned. I understand all the reasons why this
occurs, but i dont understand why its implemented this way. Because it
acts in a different way than you expect.


No, it acts in a different way than *you* expect. It does exactly what
I expect, which is why I didn't write it that way.
It seems to me that
self.instance_count should not create a new entry in the __dict__ if a
class variable of that name is already present anywhere in that objects
hierarchy.

Does that make sense?
Yes, but such behavior would make Python worse, not better. Right
now, binding instance.name *always* works(*) on the value of the name
attribute of instance. Creating a special case for when one of the
classes instance belongs to happens to have an attribute "name" would
be less consistent than the current behavior. Yes, the current
behavior is surprising to people who aren't used to dynamic
languages. But there are lots of such things - they're part of the
power of dynamic languages. People who want to program in dynamic
languages just have to get used to those things. That's a lesser price
to pay than having a language cluttered with special cases just to
avoid surprising people who aren't used to the language yet.
Again thank you for explaination.


You're welcome.

<mike

*) I almost said "binds", but that's not true. Attempting to bind an
attribute can invoke arbitrary code - but it's got the instance and
attribute name to work with. If you really wanted to, you could create
a class for which trying to set an attribute behaved as you wanted. Be
warned - it's not as easy as it looks.
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Nov 4 '05 #49
Antoon Pardon wrote:
Op 2005-11-03, Stefan Arentz schreef <st***********@gmail.com>:
Antoon Pardon <ap*****@forel.vub.ac.be> writes:

...

>No matter wat the OO model is, I don't think the following code
>exhibits sane behaviour:
>
>class A:
> a = 1
>
>b = A()
>b.a += 2
>print b.a
>print A.a
>
>Which results in
>
>3
>1

I find it confusing at first, but I do understand what happens :-)

I understand what happens too, that doesn't make it sane behaviour.
But really, what should be done different here?

I don't care what should be different. But a line with only one
referent to an object in it, shouldn't be referring to two different
objects.
It doesn't.

Yes it does. If the b.a refers to the instance variable, then an
AttributeError should be raised, because the instance variable doesn't
exist yet, so you can't add two to it.

Excuse me. The statement

a += 2

causes a to refer to a different object after the assignment than it did
before. So does the statement

self.a += 2

So why are you so concerned that the pre-assignment reference comes from
a different scope to the post-assignment reference? The fact remains
that after both assignments the rebound name can no longer (ever) be
used to refer to its former referent without a further rebinding taking
place.
If the b.a refers to the class variable then two should be added to it.
Wring, wring, wring. (Sorry, got that wrong :-)
Neither happens instead we get some hybrid in which an instance varible
is created that gets the value of class variable incrented by two.
Yes. So does this mean you also have a problem with

def f(x):
x += 2

g = 3
print f(g)

When the function call executes, the name x is bound to an object in the
call's containing scope. Is it then your contention that the augmented
assignment in the function should add two to that object, changing the
value of g?

For extra marks please explain the difference between augmented
assignment to a function argument and augmented assignment to a class
variable referenced through self.
In the line: b.a += 2, the b.a should be refering to the class variable
or the object variable but not both. So either it could raise an
attribute error or add two to the class variable. I'm not sure where this moral imperative comes from, and your arguments
singularly fail to convince me.
It does exactly what you say. It adds 2 to the a *instance variable* of
the object instance in 'b'.

There is no instance variable at that point. How can it add 2, to
something that doesn't exist at the moment.

It doesn't, it simply proceeds along the lines of all Python assignments
and resolves the name as a reference to a specific object. It then
computes a new value from the referenced object and the augmented
assignment operator's right operand, and rebinds the name to the
newly-computed value.

Please stop talking about variables.

Although augmented assignment operators have the *option* of updating
objects in place, surely not even you can require that they do so when
they are bound to an immutable object such as an integer.
It doesn't touch the *class variable* A.a which is still 1.

Why "should" it? Why, why, why? And gain, just for good measure, why?
Augmented assignment to a function argument doesn't modify the passed
object when immutable, and you have no problem with that (I know as I
write that this is just asking for trouble, and it will turn out that
you also find that behavior deeply controversial ...)

But it accesses the class variable.

Repeat after me: "Python assignment binds values to names".

When I write

class something:
a = 1
def __init__(self, val=None):
if val:
self.a += val

then in the last statement the augmented assignment rebinds "self.a"
from the class "variable" to a newly-created instance "variable". I am
of course using the word "variable" here in a Pythonic sense, rather
than in the sense that, say, a C programmer would use. In Python I
prefer to talk about binding names because talking of variables leads
people to expect that a name is bound to an area of memory whose value
is modified by assignment, but this is in fact not so.

The initial access to self.a uses the defined name resolution order to
locate a value that was bound to the name "a" in class scope. So what?
This is a long-documented fact of Python life. It's *supposed* to be
that way, dammit.

I fail to understand why this is such a problem for you. But then it's
clear from long past experience that our perceptions of Python's
execution model differ quite radically, and that I seem to find it quite
satisfactory overall, whereas you are forever banging on about what
"should" be true of Python and what Python "should" do. Which, as is
probably obvious by now, I sometimes find just a teeny bit irritating.
Kindly pardon my tetchiness.

I suppose ultimately I'm just more pragmatic than you. Plus I started
using Icon, whose assignment semantics are very similar, back in the
1970's, so Python's way of doing things fits my brain quite nicely,
thank you.

regards
Steve

PS As a total non-sequitur added for light relief at the end of what
seems even to me to be a slightly tedious post, I discover I managed to
misspell "assignment" in four distinct ways during the composition of
the above.
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Nov 4 '05 #50

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

Similar topics

106
by: A | last post by:
Hi, I have always been taught to use an inialization list for initialising data members of a class. I realize that initialsizing primitives and pointers use an inialization list is exactly the...
5
by: xuatla | last post by:
Hi, I encountered the following compile error of c++ and hope to get your help. test2.cpp: In member function `CTest CTest::operator+=(CTest&)': test2.cpp:79: error: no match for 'operator='...
5
by: Chris | last post by:
Hi, I don't get the difference between a struct and a class ! ok, I know that a struct is a value type, the other a reference type, I understand the technical differences between both, but...
9
by: NevilleDNZ | last post by:
Can anyone explain why "begin B: 123" prints, but 456 doesn't? $ /usr/bin/python2.3 x1x2.py begin A: Pre B: 123 456 begin B: 123 Traceback (most recent call last): File "x1x2.py", line 13,...
14
by: lovecreatesbea... | last post by:
Could you tell me how many class members the C++ language synthesizes for a class type? Which members in a class aren't derived from parent classes? I have read the book The C++ Programming...
20
by: tshad | last post by:
Using VS 2003, I am trying to take a class that I created to create new variable types to handle nulls and track changes to standard variable types. This is for use with database variables. This...
20
by: d.s. | last post by:
I've got an app with two classes, and one class (InventoryInfoClass) is an object within the other class (InventoryItem). I'm running into problems with trying to access (get/set) a private...
16
by: John Doe | last post by:
Hi, I wrote a small class to enumerate available networks on a smartphone : class CNetwork { public: CNetwork() {}; CNetwork(CString& netName, GUID netguid): _netname(netName),...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...

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.