Inheritance error in python 2.3.4???

F

friedmud

In trying to construct a good object model in a recent project of mine,
I ran across the following peculiarity in python 2.3.4 (haven't tried
any newer versions):

Say you have a base class that has an attribute and an accessor
function for that attribute (just a simple get). Then you inherit that
base class to make a sub class and the subclass contains a "set"
function on that same attribute. If you call the set function and give
it a value and then call the get function......... it doesn't do what
you would expect.... instead of returning the value that was "set" it
instead returns the default value of the variable in the base class!!!

BUT! If you implement the get function in the derived class it works
fine....

This, to me, is completely wrong.

I have worked up the following example to illustrate my point:

First is the way I want to do it:
######################################
bash-2.05b$ cat main.py
class baseClass(object):
__Something = "Dumb!"

def getSomething( self ):
return self.__Something

class subClass(baseClass):
def setSomething( self , aSomething ):
self.__Something = aSomething


anObject = subClass()
anObject.setSomething("Cool!")
print anObject.getSomething()

bash-2.05b$ python main.py
Dumb!
###################################

Note that it prints "Dumb!" instead of "Cool!".

Now if I re-implement getSomething in the subclass it does this:
####################################
bash-2.05b$ cat main.py
class baseClass(object):
__Something = "Dumb!"

def getSomething( self ):
return self.__Something

class subClass(baseClass):
def setSomething( self , aSomething ):
self.__Something = aSomething

def getSomething( self ):
return self.__Something


anObject = subClass()
anObject.setSomething("Cool!")
print anObject.getSomething()

bash-2.05b$ python main.py
Cool!
#######################################

Note that it now prints "Cool!" like it was supposed to in the first
place...

Hello? Am I just being retarded? To me the call to self.__Something =
aSomething should change THE ONLY instance of __Something to "Cool!".
Calling an inherited function shouldn't create a new instance of
__Something (which is what it has to be doing) and return that instead.

This is really going to cripple my object model if I have to
reimplement each one of these functions everytime! The whole point was
to have a stable "implementation base" that captures a lot of the
common functionality between the different inherited classes... and
provides a "one stop shop" for modifying a lot of the behavior. Also I
would have a lot less code because I would inherit functionality.

FYI - I am coming from a C++ background... where I do this kind of
thing often.

Someone please show me the pythonish way to do this!

Thanks!
Friedmud
 
F

Fredrik Lundh

In trying to construct a good object model in a recent project of mine,
I ran across the following peculiarity in python 2.3.4 (haven't tried
any newer versions):

Say you have a base class that has an attribute and an accessor
function for that attribute (just a simple get).

(don't use getters and setters methods in Python; use bare attributes where
you can, and properties when you need to add logic)
BUT! If you implement the get function in the derived class it works
fine....

This, to me, is completely wrong.

it works exactly as documented.
I have worked up the following example to illustrate my point:

First is the way I want to do it:
######################################
bash-2.05b$ cat main.py
class baseClass(object):
__Something = "Dumb!"

def getSomething( self ):
return self.__Something

class subClass(baseClass):
def setSomething( self , aSomething ):
self.__Something = aSomething

anObject = subClass()
anObject.setSomething("Cool!")
print anObject.getSomething()

bash-2.05b$ python main.py
Dumb!
###################################

Note that it prints "Dumb!" instead of "Cool!".

members that start with __ (two underscores) are private to the class, so
you're in fact working with two different attributes here.

see section 9.6 in the tutorial for more on this:

http://docs.python.org/tut/node11.html#SECTION0011600000000000000000

to fix your problem, rename the attribute.

</F>
 
S

Steven Bethard

class baseClass(object):
__Something = "Dumb!"

def getSomething( self ):
return self.__Something

class subClass(baseClass):
def setSomething( self , aSomething ):
self.__Something = aSomething


anObject = subClass()
anObject.setSomething("Cool!")
print anObject.getSomething()

Your mistake is using '__' names when they aren't necessary. '__' names
are mangled with the class name:

py> class B(object):
.... __x = False
.... def __init__(self):
.... self.__x = 1
.... def getx(self):
.... return self.__x
....
py> class C(object):
.... def setx(self, x):
.... self.__x = x
....
py> vars(B())
{'_B__x': 1}
py> vars(C())
{}

Note that C instances don't get __x variables because of the mangling.
Someone please show me the pythonish way to do this!

A number of suggestions:

(1) Generally, you don't need getters and setters. If you think you do,
you probably want property instead.

(2) Don't use __ names. They're a hack that doesn't really make things
private, and doesn't even avoid name collisions all the time. There are
probably a few cases where they're useful, but this is not one of them.
If you don't want attributes to show up in automatically generated
documentation, simply prefix them with a single underscore.

(3) Don't use class-level attributes as defaults for instance-level
attributes. If it's part of the instance, set it on the instance. If
it's part of the class, set it on the class. Hiding a class-level
attribute with an instance-level attribute will most likely lead to
confusion unless you *really* know what you're doing in Python.

Something like this would probably be best:

py> class BaseClass(object):
.... def __init__(self, something='Dumb!'):
.... self._something = something
.... def _getsomething(self):
.... return self._something
.... something = property(_getsomething)
....
py> class SubClass(BaseClass):
.... def _setsomething(self, something):
.... self._something = something
.... something = property(BaseClass._getsomething, _setsomething)
....
py> b = BaseClass()
py> b.something
'Dumb!'
py> b.something = 1
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
AttributeError: can't set attribute
py> s = SubClass()
py> s.something
'Dumb!'
py> s.something = 'Cool!'
py> s.something
'Cool!'

STeVe
 
F

friedmud

The problem is that I actually do need them to be private to the
outside world... but not to subclasses. I guess what I actually need
is something like "protected" in C++.... but I don't think I'm going to
get that luxury.

I think what's happening in my example is that the name mangling is
looking at the defining class instead of looking at "self"... which is
not what I expected. Which means it is accessing two different
variables (_subclass_something on the set and _baseclass__something on
the get)

Anyone know of a workaround for that?? (other than renaming the
variable so it doesn't have the two underscores?)

I guess I could just use one underscore.... but that means it is easier
for other people to get at my implementation details (which, coming
from a C++ background really bothers me).

Friedmud
 
P

Paul Rubin

The problem is that I actually do need them to be private to the
outside world... but not to subclasses. I guess what I actually need
is something like "protected" in C++.... but I don't think I'm going to
get that luxury.

The only way to make instance variables really private is to put them
in a separate process and use IPC to reach the accessors. The __xyz
convention results in deterministic name mangling that other parts of
the program can undo if they wish to.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top