Inheritance error in python 2.3.4???

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • friedmud@gmail.com

    #1

    Inheritance error in python 2.3.4???

    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(objec t):
    __Something = "Dumb!"

    def getSomething( self ):
    return self.__Somethin g

    class subClass(baseCl ass):
    def setSomething( self , aSomething ):
    self.__Somethin g = aSomething


    anObject = subClass()
    anObject.setSom ething("Cool!")
    print anObject.getSom ething()

    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(objec t):
    __Something = "Dumb!"

    def getSomething( self ):
    return self.__Somethin g

    class subClass(baseCl ass):
    def setSomething( self , aSomething ):
    self.__Somethin g = aSomething

    def getSomething( self ):
    return self.__Somethin g


    anObject = subClass()
    anObject.setSom ething("Cool!")
    print anObject.getSom ething()

    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.__Somethin g =
    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 "implementa tion 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

  • Fredrik Lundh

    #2
    Re: Inheritance error in python 2.3.4???

    friedmud@gmail. com wrote:
    [color=blue]
    > 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).[/color]

    (don't use getters and setters methods in Python; use bare attributes where
    you can, and properties when you need to add logic)
    [color=blue]
    > BUT! If you implement the get function in the derived class it works
    > fine....
    >
    > This, to me, is completely wrong.[/color]

    it works exactly as documented.
    [color=blue]
    > 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(objec t):
    > __Something = "Dumb!"
    >
    > def getSomething( self ):
    > return self.__Somethin g
    >
    > class subClass(baseCl ass):
    > def setSomething( self , aSomething ):
    > self.__Somethin g = aSomething
    >
    > anObject = subClass()
    > anObject.setSom ething("Cool!")
    > print anObject.getSom ething()
    >
    > bash-2.05b$ python main.py
    > Dumb!
    > ############### ############### #####
    >
    > Note that it prints "Dumb!" instead of "Cool!".[/color]

    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:



    to fix your problem, rename the attribute.

    </F>



    Comment

    • Steven Bethard

      #3
      Re: Inheritance error in python 2.3.4???

      friedmud@gmail. com wrote:[color=blue]
      > class baseClass(objec t):
      > __Something = "Dumb!"
      >
      > def getSomething( self ):
      > return self.__Somethin g
      >
      > class subClass(baseCl ass):
      > def setSomething( self , aSomething ):
      > self.__Somethin g = aSomething
      >
      >
      > anObject = subClass()
      > anObject.setSom ething("Cool!")
      > print anObject.getSom ething()[/color]

      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.
      [color=blue]
      > Someone please show me the pythonish way to do this![/color]

      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(objec t):
      .... def __init__(self, something='Dumb !'):
      .... self._something = something
      .... def _getsomething(s elf):
      .... return self._something
      .... something = property(_getso mething)
      ....
      py> class SubClass(BaseCl ass):
      .... def _setsomething(s elf, something):
      .... self._something = something
      .... something = property(BaseCl ass._getsomethi ng, _setsomething)
      ....
      py> b = BaseClass()
      py> b.something
      'Dumb!'
      py> b.something = 1
      Traceback (most recent call last):
      File "<interacti ve 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

      Comment

      • friedmud@gmail.com

        #4
        Re: Inheritance error in python 2.3.4???

        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_some thing on the set and _baseclass__som ething 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

        Comment

        • Paul Rubin

          #5
          Re: Inheritance error in python 2.3.4???

          "friedmud@gmail .com" <friedmud@gmail .com> writes:[color=blue]
          > 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.[/color]

          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.

          Comment

          Working...