Graham said:
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:
if you changed
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.