Class Encapsulation Errors in Python 2.3.3

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Tim Henderson

    #1

    Class Encapsulation Errors in Python 2.3.3

    Hi i have some errors in my code that i can't find the reason for here
    is the error

    i have 3 classes:
    -Song
    -Album
    -Artist

    Song has 3 pieces of data with getter and setter meathods:
    -Name | i.e. song name
    -Album
    -Artist

    Album has 3 pieces of data:
    -Songs | a list of Song() instances
    -Name
    -Artist

    Artist has 4 pieces of Data:
    -Name
    -Songs | a list of Song() instances
    -Albums |a list of Album() instances
    -unknownAlbum | the album a song is put into if it doesn't have an
    album


    now the problem arises when i add songs into multiple albums like
    this:
    Code:
    ------------------------------------------------
    s = Song('song 1')
    s2 = Song('song 2')

    a = Album('hey')
    a.addSong(s)
    ab = Album('yeayea')
    print a
    print ab
    ab.addSong(s2)
    print
    print a
    print ab
    ------------------------------------------------
    Output:
    *************** *************** *************** ***
    hey song 1 hey |
    yeayea song 1 hey |

    hey song 1 hey | song 2 yeayea |
    yeayea song 1 hey | song 2 yeayea |
    *************** *************** *************** ***

    the "hey" album has "song 1" as it is suppose to however so does
    "yeayea" even though when "song 1" was put in the album "hey" 'yeayea'
    didn't even exist yet.

    Why is this happening. I checked the actually memory refrence for each
    instance and they are different, these supposidly isolated different
    intances of clases are linked for some inexplicable reason.

    Does any one know why this is happening and how i can fix it?

    cheers
    Tim Henderson
  • Jeff Shannon

    #2
    Re: Class Encapsulation Errors in Python 2.3.3

    Tim Henderson wrote:
    [color=blue]
    >Hi i have some errors in my code that i can't find the reason for here
    >is the error
    >[...]
    >the "hey" album has "song 1" as it is suppose to however so does
    >"yeayea" even though when "song 1" was put in the album "hey" 'yeayea'
    >didn't even exist yet.
    >
    >[/color]

    I can think of two likely reasons offhand.

    The first is that you're storing the songlist as a class attribute,
    rather than as an instance attribute. The second is that you're using a
    mutable default value in a method call. (I'm guessing that it's
    probably this second reason.)

    Do you have something like this?

    class Album:
    def __init__(self, songs=[]):
    self.songs = songs
    # ...

    The problem here is that default values in functions/methods are
    evaluated at *compile* time. That means that this creates a single
    empty list, which is used as the default value for *every* Album
    instance. When you mutate that list, then every instance with a
    reference to that list shows the changes.

    The other possibility (class attribute rather than instance attribute)
    would happen if you have something like this:

    class Album:
    songs = []
    def __init__(self, ...):
    # ...

    In this case, again, you have a single empty list that will end up being
    shared by all Album instances.

    What you probably want to do in this case is this:

    class Album:
    def __init__(self, songs=None)
    if songs is None:
    songs = []
    self.songs = songs
    # ...

    This will allow you to have an optional songs parameter in the
    initializer, but still ensure that every instance gets an independent
    empty list if nothing's passed in.

    Jeff Shannon
    Technician/Programmer
    Credit International

    Comment

    • Shalabh Chaturvedi

      #3
      Re: Class Encapsulation Errors in Python 2.3.3

      Tim Henderson wrote:

      <snip>[color=blue]
      >
      > now the problem arises when i add songs into multiple albums like
      > this:
      > Code:
      > ------------------------------------------------
      > s = Song('song 1')
      > s2 = Song('song 2')
      >
      > a = Album('hey')
      > a.addSong(s)
      > ab = Album('yeayea')
      > print a
      > print ab
      > ab.addSong(s2)
      > print
      > print a
      > print ab
      > ------------------------------------------------
      > Output:
      > *************** *************** *************** ***
      > hey song 1 hey |
      > yeayea song 1 hey |
      >
      > hey song 1 hey | song 2 yeayea |
      > yeayea song 1 hey | song 2 yeayea |
      > *************** *************** *************** ***
      >[/color]
      <snip>

      Defnition of addSong() would help in determining the real cause. Taking
      a guess here but it looks like the 'list' you store the songs in is
      shared across all albums. Typically this happens if you define the list
      in the class itself making it a class attribute - and not in __init__().
      Please show us the code of Album to help you further.

      Shalabh

      Comment

      • Dan Perl

        #4
        Re: Class Encapsulation Errors in Python 2.3.3

        Please post the implementation of the Album class. The problem is coming
        probably from the way the Songs list is defined or initialized. That is,
        the definition or the initialization probably cause the list object that is
        assigned to Songs to be shared by all the Album instances. But I (and
        likely no one else) can say exactly what the problem is without seing the
        code.

        Dan

        "Tim Henderson" <tim.tadh@gmail .com> wrote in message
        news:47f7cc78.0 411182007.1b77c 2b1@posting.goo gle.com...[color=blue]
        > Hi i have some errors in my code that i can't find the reason for here
        > is the error
        >
        > i have 3 classes:
        > -Song
        > -Album
        > -Artist
        >
        > Song has 3 pieces of data with getter and setter meathods:
        > -Name | i.e. song name
        > -Album
        > -Artist
        >
        > Album has 3 pieces of data:
        > -Songs | a list of Song() instances
        > -Name
        > -Artist
        >
        > Artist has 4 pieces of Data:
        > -Name
        > -Songs | a list of Song() instances
        > -Albums |a list of Album() instances
        > -unknownAlbum | the album a song is put into if it doesn't have an
        > album
        >
        >
        > now the problem arises when i add songs into multiple albums like
        > this:
        > Code:
        > ------------------------------------------------
        > s = Song('song 1')
        > s2 = Song('song 2')
        >
        > a = Album('hey')
        > a.addSong(s)
        > ab = Album('yeayea')
        > print a
        > print ab
        > ab.addSong(s2)
        > print
        > print a
        > print ab
        > ------------------------------------------------
        > Output:
        > *************** *************** *************** ***
        > hey song 1 hey |
        > yeayea song 1 hey |
        >
        > hey song 1 hey | song 2 yeayea |
        > yeayea song 1 hey | song 2 yeayea |
        > *************** *************** *************** ***
        >
        > the "hey" album has "song 1" as it is suppose to however so does
        > "yeayea" even though when "song 1" was put in the album "hey" 'yeayea'
        > didn't even exist yet.
        >
        > Why is this happening. I checked the actually memory refrence for each
        > instance and they are different, these supposidly isolated different
        > intances of clases are linked for some inexplicable reason.
        >
        > Does any one know why this is happening and how i can fix it?
        >
        > cheers
        > Tim Henderson[/color]


        Comment

        • Duncan Booth

          #5
          Re: Class Encapsulation Errors in Python 2.3.3

          Jeff Shannon wrote:
          [color=blue]
          > The problem here is that default values in functions/methods are
          > evaluated at *compile* time.[/color]

          Actually, no. The default values in functions/methods are evaluated when
          the def statement is *executed* not when anything is compiled. If you
          execute the def multiple times the default values are recalculated each
          time.

          Comment

          • Carlos Ribeiro

            #6
            Re: Class Encapsulation Errors in Python 2.3.3

            On 19 Nov 2004 10:44:56 GMT, Duncan Booth <duncan.booth@i nvalid.invalid> wrote:[color=blue]
            > Jeff Shannon wrote:
            >[color=green]
            > > The problem here is that default values in functions/methods are
            > > evaluated at *compile* time.[/color]
            >
            > Actually, no. The default values in functions/methods are evaluated when
            > the def statement is *executed* not when anything is compiled. If you
            > execute the def multiple times the default values are recalculated each
            > time.[/color]

            Be careful :-) default values are calculated when the def statement is
            executed.... which is not the same as to say that they are calculated
            every time the function is called.

            To explain it better. In a conventional Python program, the def
            statement is executed only once, when a module is loaded, for example.
            In this case, default values are stored as attributes of the function
            object. In this case, the value of the default never changes.

            *If* for some reason your code runs over the def statement multiple
            times, *then* a new function object will be created with new default
            values. For example, this can happen if you force-reload a module, or
            def the function inside a for loop (weird but it works):

            for i in (1,2,3):
            def print_i(x=i):
            print x,i
            [color=blue][color=green][color=darkred]
            >>> print_i()[/color][/color][/color]
            3 3[color=blue][color=green][color=darkred]
            >>> i = 0
            >>> print_i()[/color][/color][/color]
            3 0

            --
            Carlos Ribeiro
            Consultoria em Projetos
            blog: http://rascunhosrotos.blogspot.com
            blog: http://pythonnotes.blogspot.com
            mail: carribeiro@gmai l.com
            mail: carribeiro@yaho o.com

            Comment

            • Duncan Booth

              #7
              Re: Class Encapsulation Errors in Python 2.3.3

              > On 19 Nov 2004 10:44:56 GMT, Duncan Booth <duncan.booth@i nvalid.invalid>[color=blue]
              > wrote: > Jeff Shannon wrote: > > > The problem here is that default values
              > in functions/methods are > > evaluated at *compile* time. > > Actually,
              > no. The default values in functions/methods are evaluated when > the def
              > statement is *executed* not when anything is compiled. If you > execute
              > the def multiple times the default values are recalculated each > time.
              >
              > Be careful :-) default values are calculated when the def statement is
              > executed.... which is not the same as to say that they are calculated
              > every time the function is called.
              >[/color]
              <rest of explanation snipped>

              I actually thought I had stated the case fairly clearly, but I
              suppose understanding what you think you have said isn't the same as
              communicating it to the audience. :)


              --
              Duncan Booth
              duncan.booth@su ttoncourtenay.o rg.uk


              Comment

              • Tim Henderson

                #8
                Re: Class Encapsulation Errors in Python 2.3.3

                Here is a partial implementation, i don't have the code with me but
                this is how the songs list is made

                code:
                -------------------------------------------
                class Album:

                name = ''
                songs = []
                artist = ''

                def __init__(self, name, artist):

                self.name = name
                self.artist = artist
                -------------------------------------------

                after reading the code i came up with a possible solution:

                possible code:
                -------------------------------------------
                class Album:

                name = ''
                songs = False
                artist = ''

                def __init__(self, name, artist):

                self.name = name
                self.artist = artist
                if songs == False:
                songs = []
                ------------------------------------------


                i will test this when i get home, and update the thread with results

                cheers
                Tim Henderson



                "Dan Perl" <danperl@rogers .com> wrote in message news:<nrudnceoV IIV5QDcRVn-hw@rogers.com>. ..[color=blue]
                > Please post the implementation of the Album class. The problem is coming
                > probably from the way the Songs list is defined or initialized. That is,
                > the definition or the initialization probably cause the list object that is
                > assigned to Songs to be shared by all the Album instances. But I (and
                > likely no one else) can say exactly what the problem is without seing the
                > code.
                >
                > Dan
                >
                > "Tim Henderson" <tim.tadh@gmail .com> wrote in message
                > news:47f7cc78.0 411182007.1b77c 2b1@posting.goo gle.com...[color=green]
                > > Hi i have some errors in my code that i can't find the reason for here
                > > is the error
                > >
                > > i have 3 classes:
                > > -Song
                > > -Album
                > > -Artist
                > >
                > > Song has 3 pieces of data with getter and setter meathods:
                > > -Name | i.e. song name
                > > -Album
                > > -Artist
                > >
                > > Album has 3 pieces of data:
                > > -Songs | a list of Song() instances
                > > -Name
                > > -Artist
                > >
                > > Artist has 4 pieces of Data:
                > > -Name
                > > -Songs | a list of Song() instances
                > > -Albums |a list of Album() instances
                > > -unknownAlbum | the album a song is put into if it doesn't have an
                > > album
                > >
                > >
                > > now the problem arises when i add songs into multiple albums like
                > > this:
                > > Code:
                > > ------------------------------------------------
                > > s = Song('song 1')
                > > s2 = Song('song 2')
                > >
                > > a = Album('hey')
                > > a.addSong(s)
                > > ab = Album('yeayea')
                > > print a
                > > print ab
                > > ab.addSong(s2)
                > > print
                > > print a
                > > print ab
                > > ------------------------------------------------
                > > Output:
                > > *************** *************** *************** ***
                > > hey song 1 hey |
                > > yeayea song 1 hey |
                > >
                > > hey song 1 hey | song 2 yeayea |
                > > yeayea song 1 hey | song 2 yeayea |
                > > *************** *************** *************** ***
                > >
                > > the "hey" album has "song 1" as it is suppose to however so does
                > > "yeayea" even though when "song 1" was put in the album "hey" 'yeayea'
                > > didn't even exist yet.
                > >
                > > Why is this happening. I checked the actually memory refrence for each
                > > instance and they are different, these supposidly isolated different
                > > intances of clases are linked for some inexplicable reason.
                > >
                > > Does any one know why this is happening and how i can fix it?
                > >
                > > cheers
                > > Tim Henderson[/color][/color]

                Comment

                • Jeff Shannon

                  #9
                  Re: Class Encapsulation Errors in Python 2.3.3

                  Duncan Booth wrote:
                  [color=blue]
                  >Jeff Shannon wrote:
                  >
                  >
                  >[color=green]
                  >>The problem here is that default values in functions/methods are
                  >>evaluated at *compile* time.
                  >>
                  >>[/color]
                  >
                  >Actually, no. The default values in functions/methods are evaluated when
                  >the def statement is *executed* not when anything is compiled. If you
                  >execute the def multiple times the default values are recalculated each
                  >time.
                  >
                  >[/color]

                  Hm, true, I misspoke. Perhaps it's clearest to say that the default
                  values are evaluated when the function/method object is *created*, not
                  when it is called.

                  Functions are created when the def statement is executed; this is
                  usually one of the first things that happens when a simple module/script
                  is imported/run, but it can happen at other times as well. Referring to
                  the creation of function objects as compilation, as I did, is
                  technically inaccurate (compilation being the conversion of text source
                  code to bytecode, which is later executed). I do know the difference,
                  but tend to conflate them out of laziness in cases where the distinction
                  has little practical difference. (In this case, practical differences
                  are usually only significant when function/class definitions are dynamic
                  and/or some sort of deep magic is being employed, neither of which are
                  typical for me to need...) But I should indeed be more careful about
                  how I state that, because while *I* know what I meant, it doesn't follow
                  that someone else reading this and looking for guidance won't get bitten
                  by my laziness. :)

                  Jeff Shannon
                  Technician/Programmer
                  Credit International

                  Comment

                  • Jeff Shannon

                    #10
                    Re: Class Encapsulation Errors in Python 2.3.3

                    Tim Henderson wrote:
                    [color=blue]
                    >Here is a partial implementation, i don't have the code with me but
                    >this is how the songs list is made
                    >
                    >code:
                    >-------------------------------------------
                    >class Album:
                    >
                    > name = ''
                    > songs = []
                    > artist = ''
                    >
                    > def __init__(self, name, artist):
                    >
                    > self.name = name
                    > self.artist = artist
                    >-------------------------------------------
                    >
                    >[/color]

                    Yes, this gives you class-level attributes, shared by all instances of
                    Album. In the case of name and artist, you're then creating
                    instance-level attributes ('self.name = name') which shadow the
                    class-level attribute. But you never bind 'songs' on the instance, so
                    you're just modifying the class-level (shared) songs list.
                    [color=blue]
                    >after reading the code i came up with a possible solution:
                    >
                    >possible code:
                    >-------------------------------------------
                    >class Album:
                    >
                    > name = ''
                    > songs = False
                    > artist = ''
                    >
                    > def __init__(self, name, artist):
                    >
                    > self.name = name
                    > self.artist = artist
                    > if songs == False:
                    > songs = []
                    >------------------------------------------
                    >
                    >[/color]

                    That'll almost work -- within __init__(), you need to refer to songs as
                    self.songs (both times). But why bother with creating the class-level
                    attributes? If you just set them in __init__(), all will be fine.

                    class Album:

                    def __init__(self, name, artist):

                    self.name = name
                    self.artist = artist
                    self.songs = []

                    will have the same net effect as your code, for the vast majority of
                    common purposes. (There are corner cases where having class-level
                    defaults is helpful, but I sincerely doubt that this is true for your
                    program.)

                    Jeff Shannon
                    Technician/Programmer
                    Credit International


                    Comment

                    • Dan Perl

                      #11
                      Re: Class Encapsulation Errors in Python 2.3.3

                      I fully agree with Jeff and I will make a couple of other small points.

                      I saw you meant to use 'False' as a default value for the class-level
                      attribute 'songs'. 'None' is the normal value for initializations like
                      that. It's a small point but it kind of stuck in my mind as being unusual.

                      If you still choose to use class-level attributes and to initialize them
                      with default values, you have to keep in mind that you will encounter this
                      kind of problem with all the initializations that are using complex types.
                      That means lists like in the case of 'songs', but also strings like in the
                      case of 'name' and 'artist'. Jeff pointed out in an earlier posting that
                      there are also other ways you could get into trouble like this and it should
                      be made clear that this is true for all the complex types, not only lists.

                      Dan

                      "Jeff Shannon" <jeff@ccvcorp.c om> wrote in message
                      news:10pskhnjt2 met3f@corp.supe rnews.com...[color=blue]
                      > Tim Henderson wrote:
                      >[color=green]
                      >>Here is a partial implementation, i don't have the code with me but
                      >>this is how the songs list is made
                      >>
                      >>code:
                      >>-------------------------------------------
                      >>class Album:
                      >> name = ''
                      >> songs = []
                      >> artist = ''
                      >>
                      >> def __init__(self, name, artist):
                      >> self.name = name
                      >> self.artist = artist
                      >>-------------------------------------------
                      >>[/color]
                      >
                      > Yes, this gives you class-level attributes, shared by all instances of
                      > Album. In the case of name and artist, you're then creating
                      > instance-level attributes ('self.name = name') which shadow the
                      > class-level attribute. But you never bind 'songs' on the instance, so
                      > you're just modifying the class-level (shared) songs list.
                      >[color=green]
                      >>after reading the code i came up with a possible solution:
                      >>
                      >>possible code:
                      >>-------------------------------------------
                      >>class Album:
                      >> name = ''
                      >> songs = False
                      >> artist = ''
                      >>
                      >> def __init__(self, name, artist):
                      >> self.name = name
                      >> self.artist = artist
                      >> if songs == False:
                      >> songs = []
                      >>------------------------------------------
                      >>[/color]
                      >
                      > That'll almost work -- within __init__(), you need to refer to songs as
                      > self.songs (both times). But why bother with creating the class-level
                      > attributes? If you just set them in __init__(), all will be fine.
                      >
                      > class Album:
                      >
                      > def __init__(self, name, artist):
                      >
                      > self.name = name
                      > self.artist = artist
                      > self.songs = []
                      >
                      > will have the same net effect as your code, for the vast majority of
                      > common purposes. (There are corner cases where having class-level
                      > defaults is helpful, but I sincerely doubt that this is true for your
                      > program.)
                      >
                      > Jeff Shannon
                      > Technician/Programmer
                      > Credit International
                      >
                      >[/color]


                      Comment

                      • Dan Perl

                        #12
                        Re: Class Encapsulation Errors in Python 2.3.3

                        Errata: instead of "complex types" please read "mutable types".

                        "Dan Perl" <danperl@rogers .com> wrote in message
                        news:z_ydneo0T-HFxQPcRVn-ug@rogers.com.. .[color=blue]
                        >I fully agree with Jeff and I will make a couple of other small points.
                        >
                        > I saw you meant to use 'False' as a default value for the class-level
                        > attribute 'songs'. 'None' is the normal value for initializations like
                        > that. It's a small point but it kind of stuck in my mind as being
                        > unusual.
                        >
                        > If you still choose to use class-level attributes and to initialize them
                        > with default values, you have to keep in mind that you will encounter this
                        > kind of problem with all the initializations that are using complex types.
                        > That means lists like in the case of 'songs', but also strings like in the
                        > case of 'name' and 'artist'. Jeff pointed out in an earlier posting that
                        > there are also other ways you could get into trouble like this and it
                        > should be made clear that this is true for all the complex types, not only
                        > lists.
                        >
                        > Dan
                        >
                        > "Jeff Shannon" <jeff@ccvcorp.c om> wrote in message
                        > news:10pskhnjt2 met3f@corp.supe rnews.com...[color=green]
                        >> Tim Henderson wrote:
                        >>[color=darkred]
                        >>>Here is a partial implementation, i don't have the code with me but
                        >>>this is how the songs list is made
                        >>>
                        >>>code:
                        >>>-------------------------------------------
                        >>>class Album:
                        >>> name = ''
                        >>> songs = []
                        >>> artist = ''
                        >>>
                        >>> def __init__(self, name, artist):
                        >>> self.name = name
                        >>> self.artist = artist
                        >>>-------------------------------------------
                        >>>[/color]
                        >>
                        >> Yes, this gives you class-level attributes, shared by all instances of
                        >> Album. In the case of name and artist, you're then creating
                        >> instance-level attributes ('self.name = name') which shadow the
                        >> class-level attribute. But you never bind 'songs' on the instance, so
                        >> you're just modifying the class-level (shared) songs list.
                        >>[color=darkred]
                        >>>after reading the code i came up with a possible solution:
                        >>>
                        >>>possible code:
                        >>>-------------------------------------------
                        >>>class Album:
                        >>> name = ''
                        >>> songs = False
                        >>> artist = ''
                        >>>
                        >>> def __init__(self, name, artist):
                        >>> self.name = name
                        >>> self.artist = artist
                        >>> if songs == False:
                        >>> songs = []
                        >>>------------------------------------------
                        >>>[/color]
                        >>
                        >> That'll almost work -- within __init__(), you need to refer to songs as
                        >> self.songs (both times). But why bother with creating the class-level
                        >> attributes? If you just set them in __init__(), all will be fine.
                        >>
                        >> class Album:
                        >>
                        >> def __init__(self, name, artist):
                        >>
                        >> self.name = name
                        >> self.artist = artist
                        >> self.songs = []
                        >>
                        >> will have the same net effect as your code, for the vast majority of
                        >> common purposes. (There are corner cases where having class-level
                        >> defaults is helpful, but I sincerely doubt that this is true for your
                        >> program.)
                        >>
                        >> Jeff Shannon
                        >> Technician/Programmer
                        >> Credit International
                        >>
                        >>[/color]
                        >
                        >[/color]


                        Comment

                        • Terry Reedy

                          #13
                          Re: Class Encapsulation Errors in Python 2.3.3


                          "Tim Henderson" <tim.tadh@gmail .com> wrote in message
                          news:47f7cc78.0 411191005.7b793 bf@posting.goog le.com...[color=blue]
                          > Here is a partial implementation, i don't have the code with me but
                          > this is how the songs list is made
                          >
                          > code:
                          > -------------------------------------------
                          > class Album:
                          >
                          > name = ''
                          > songs = []
                          > artist = ''[/color]

                          Delete all three of these lines. The name and artist lines have no effect
                          since they are over-ridden by the local variables of the same name in
                          __init__. The songs line makes songs an attribute of the Album class,
                          which you do not want, instead of each Album instance, which you do.
                          [color=blue]
                          > def __init__(self, name, artist):[/color]

                          If you want artist to be optional, augment to ..., artist = ''
                          [color=blue]
                          >
                          > self.name = name
                          > self.artist = artist[/color]

                          add
                          self.songs = []
                          now songs is an attribute of the album.
                          [color=blue]
                          > possible revision[/color]
                          [snip][color=blue]
                          > if songs == False:
                          > songs = [][/color]

                          This assignment makes songs a local variable in the init function, which is
                          again what you don't want. In fact, the test will fail since local var
                          songs will not have a value before the assignment. See proper fix above.

                          Terry J. Reedy



                          Comment

                          • Dan Perl

                            #14
                            Re: Class Encapsulation Errors in Python 2.3.3

                            Better make another correction before someone else totally embarrasses me.
                            I was wrong, strings are not mutable.

                            That immediately made me wonder why strings are immutable. But it turns out
                            that question has already been beaten to death, not too long ago
                            (http://groups.google.com/groups?hl=e...com%26rnum%3D2)

                            Dan

                            "Dan Perl" <danperl@rogers .com> wrote in message
                            news:06adnWRWPN fexAPcRVn-gA@rogers.com.. .[color=blue]
                            > Errata: instead of "complex types" please read "mutable types".
                            >
                            > "Dan Perl" <danperl@rogers .com> wrote in message
                            > news:z_ydneo0T-HFxQPcRVn-ug@rogers.com.. .[color=green]
                            >>I fully agree with Jeff and I will make a couple of other small points.
                            >>
                            >> I saw you meant to use 'False' as a default value for the class-level
                            >> attribute 'songs'. 'None' is the normal value for initializations like
                            >> that. It's a small point but it kind of stuck in my mind as being
                            >> unusual.
                            >>
                            >> If you still choose to use class-level attributes and to initialize them
                            >> with default values, you have to keep in mind that you will encounter
                            >> this kind of problem with all the initializations that are using complex
                            >> types. That means lists like in the case of 'songs', but also strings
                            >> like in the case of 'name' and 'artist'. Jeff pointed out in an earlier
                            >> posting that there are also other ways you could get into trouble like
                            >> this and it should be made clear that this is true for all the complex
                            >> types, not only lists.
                            >>
                            >> Dan
                            >>
                            >> "Jeff Shannon" <jeff@ccvcorp.c om> wrote in message
                            >> news:10pskhnjt2 met3f@corp.supe rnews.com...[color=darkred]
                            >>> Tim Henderson wrote:
                            >>>
                            >>>>Here is a partial implementation, i don't have the code with me but
                            >>>>this is how the songs list is made
                            >>>>
                            >>>>code:
                            >>>>-------------------------------------------
                            >>>>class Album:
                            >>>> name = ''
                            >>>> songs = []
                            >>>> artist = ''
                            >>>>
                            >>>> def __init__(self, name, artist):
                            >>>> self.name = name
                            >>>> self.artist = artist
                            >>>>-------------------------------------------
                            >>>>
                            >>>
                            >>> Yes, this gives you class-level attributes, shared by all instances of
                            >>> Album. In the case of name and artist, you're then creating
                            >>> instance-level attributes ('self.name = name') which shadow the
                            >>> class-level attribute. But you never bind 'songs' on the instance, so
                            >>> you're just modifying the class-level (shared) songs list.
                            >>>
                            >>>>after reading the code i came up with a possible solution:
                            >>>>
                            >>>>possible code:
                            >>>>-------------------------------------------
                            >>>>class Album:
                            >>>> name = ''
                            >>>> songs = False
                            >>>> artist = ''
                            >>>>
                            >>>> def __init__(self, name, artist):
                            >>>> self.name = name
                            >>>> self.artist = artist
                            >>>> if songs == False:
                            >>>> songs = []
                            >>>>------------------------------------------
                            >>>>
                            >>>
                            >>> That'll almost work -- within __init__(), you need to refer to songs as
                            >>> self.songs (both times). But why bother with creating the class-level
                            >>> attributes? If you just set them in __init__(), all will be fine.
                            >>>
                            >>> class Album:
                            >>>
                            >>> def __init__(self, name, artist):
                            >>>
                            >>> self.name = name
                            >>> self.artist = artist
                            >>> self.songs = []
                            >>>
                            >>> will have the same net effect as your code, for the vast majority of
                            >>> common purposes. (There are corner cases where having class-level
                            >>> defaults is helpful, but I sincerely doubt that this is true for your
                            >>> program.)
                            >>>
                            >>> Jeff Shannon
                            >>> Technician/Programmer
                            >>> Credit International
                            >>>
                            >>>[/color]
                            >>
                            >>[/color]
                            >
                            >[/color]


                            Comment

                            • Tim Henderson

                              #15
                              Re: Class Encapsulation Errors in Python 2.3.3

                              I have solved the problem, it was indeed declaring the songs list in
                              the wrong place. I moved the declaration and the songs list no longer
                              acts like a static variable. I am going to have to read up on how the
                              interpreter evaluates variables declared there.


                              below is the new code, In this code the songs list no longer act like
                              the same variable.

                              code:
                              ------------------------------------------------------------------
                              class Song:

                              name = ''
                              artist = ''
                              album = ''

                              def __init__(self, name, artist='', album=''):

                              self.name = name
                              self.artist = artist
                              self.album = album


                              def setName(self, name):
                              self.name = name

                              def getName(self):
                              return self.name

                              def setArtist(self, artist):
                              self.artist = artist

                              def getArtist(self) :
                              return self.artist

                              def setAlbum(self, album):
                              self.album = album

                              def getAlbum(self):
                              return self.album

                              def __str__(self):
                              return self.name + ' ' + self.artist + ' ' + self.album + '
                              '


                              class Album:

                              songs = False
                              name = ''
                              artist = ''

                              def __init__(self, name, artist=''):
                              if not self.songs: self.songs = []
                              self.name = name
                              self.artist = artist

                              def setName(self, name):

                              self.name = name
                              for x in songs:
                              x.setAlbum(self .name)

                              def getName(self):
                              return self.name

                              def setArtist(self, artist):
                              self.artist = artist
                              for x in self.songs:
                              x.setArtist(sel f.artist)

                              def getArtist(self) :
                              return self.artist

                              def addSong(self, song): #must be song class
                              #print 'NAME: ' + self.name
                              song.setAlbum(s elf.name)
                              song.setArtist( self.artist)
                              self.songs.appe nd(song)

                              def deleteSong(self , name):

                              x = 0
                              while x < self.songs.__le n__():
                              cname = self.songs[x].getName()

                              if cname == name:
                              del self.songs[x]
                              return True

                              x += 1

                              return False

                              def __str__(self):
                              val = self.name + '\t'

                              for x in self.songs:
                              val += x.__str__() + " | "

                              return val
                              def getSongs(self):
                              return self.songs







                              class Artist:

                              name = ''
                              songs = False #this is only really for deleting songs
                              albums = False
                              unknownAlbum = '' #set in init

                              def __init__(self, name):
                              if not self.songs: self.songs = []
                              if not self.albums: self.albums = []
                              self.unknownAlb um = Album('unknown' )
                              self.albums.app end(self.unknow nAlbum)
                              self.name = name
                              self.unknownAlb um.setArtist(se lf.name)

                              def setName(self, name):
                              for x in albums:
                              self.name = name

                              def getName(self):
                              return self.name

                              def addSong(self, song): #must be song class
                              ##~ print 'NAME:\t:'+self .name
                              song.setArtist( self.name)

                              a = song.getAlbum()
                              for x in self.albums:
                              if a == x.getName():
                              ##~ print '--------'
                              ##~ print a + ' ' + x.getName()
                              ##~ print x
                              ##~ print song
                              ##~ print '--------'
                              x.addSong(song)
                              print 'ya'
                              self.songs.appe nd(song)
                              print self.songs
                              ##~ print '********'
                              ##~ print x
                              ##~ print self.unknownAlb um
                              ##~ print '********'
                              return

                              if a == '':

                              self.unknownAlb um.addSong(song )
                              #print "yo " + song.__str__()
                              a = True
                              self.songs.appe nd(song)
                              return

                              elif a != True:
                              #print 'yea ' + song.__str__()
                              newAlbum = Album(a)
                              newAlbum.setArt ist(self.name)
                              self.albums.app end(newAlbum)

                              print self.addSong(so ng)
                              ##~ print "@"+
                              self.albums[self.albums.__l en__()-1].__str__()
                              return



                              def deleteSong(self , name): #give song name

                              x = 0
                              while x < self.songs.__le n__():
                              cname = self.songs[x].getName()

                              if cname == name:
                              a = self.songs[x].getAlbum()

                              for calbum in self.albums:
                              if a == calbum.getName( ):
                              calbum.deleteSo ng(name)

                              del self.songs[x]
                              return True

                              x += 1

                              return False

                              def addAlbum(self, Album): #must be album class
                              aSongs = Album.getSongs( )
                              for song in aSongs: self.songs.appe nd(song)
                              Album.setArtist (self.name)
                              self.albums.app end(Album)

                              def deleteAlbum(sel f, name):

                              x = 0
                              while x < self.albums.__l en__():
                              cname = self.albums[x].getName()

                              if cname == name:

                              cSongs = self.albums[x].getSongs()

                              for cSong in cSongs:
                              self.deleteSong (cSong.getName( ))

                              del self.albums[x]
                              return True

                              x += 1

                              return False

                              def __str__(self):
                              val = ''
                              for x in self.albums:
                              val += x.__str__() + '\n'


                              return val

                              def getSongs(self): return self.songs
                              def getAlbums(self) : return self.albums

                              #test stuff
                              s = Song('song 1')
                              s2 = Song('song 2')

                              a = Album('hey')
                              a.addSong(s)

                              art = Artist('Tim')

                              art.addAlbum(a)
                              art.addSong(s2)

                              print art
                              art.deleteSong( s.getName())

                              print art
                              ---------------------------------------------------------------------


                              cheers
                              Tim Henderson








                              "Dan Perl" <danperl@rogers .com> wrote in message news:<nrudnceoV IIV5QDcRVn-hw@rogers.com>. ..[color=blue]
                              > Please post the implementation of the Album class. The problem is coming
                              > probably from the way the Songs list is defined or initialized. That is,
                              > the definition or the initialization probably cause the list object that is
                              > assigned to Songs to be shared by all the Album instances. But I (and
                              > likely no one else) can say exactly what the problem is without seing the
                              > code.
                              >
                              > Dan
                              >
                              > "Tim Henderson" <tim.tadh@gmail .com> wrote in message
                              > news:47f7cc78.0 411182007.1b77c 2b1@posting.goo gle.com...[color=green]
                              > > Hi i have some errors in my code that i can't find the reason for here
                              > > is the error
                              > >
                              > > i have 3 classes:
                              > > -Song
                              > > -Album
                              > > -Artist
                              > >
                              > > Song has 3 pieces of data with getter and setter meathods:
                              > > -Name | i.e. song name
                              > > -Album
                              > > -Artist
                              > >
                              > > Album has 3 pieces of data:
                              > > -Songs | a list of Song() instances
                              > > -Name
                              > > -Artist
                              > >
                              > > Artist has 4 pieces of Data:
                              > > -Name
                              > > -Songs | a list of Song() instances
                              > > -Albums |a list of Album() instances
                              > > -unknownAlbum | the album a song is put into if it doesn't have an
                              > > album
                              > >
                              > >
                              > > now the problem arises when i add songs into multiple albums like
                              > > this:
                              > > Code:
                              > > ------------------------------------------------
                              > > s = Song('song 1')
                              > > s2 = Song('song 2')
                              > >
                              > > a = Album('hey')
                              > > a.addSong(s)
                              > > ab = Album('yeayea')
                              > > print a
                              > > print ab
                              > > ab.addSong(s2)
                              > > print
                              > > print a
                              > > print ab
                              > > ------------------------------------------------
                              > > Output:
                              > > *************** *************** *************** ***
                              > > hey song 1 hey |
                              > > yeayea song 1 hey |
                              > >
                              > > hey song 1 hey | song 2 yeayea |
                              > > yeayea song 1 hey | song 2 yeayea |
                              > > *************** *************** *************** ***
                              > >
                              > > the "hey" album has "song 1" as it is suppose to however so does
                              > > "yeayea" even though when "song 1" was put in the album "hey" 'yeayea'
                              > > didn't even exist yet.
                              > >
                              > > Why is this happening. I checked the actually memory refrence for each
                              > > instance and they are different, these supposidly isolated different
                              > > intances of clases are linked for some inexplicable reason.
                              > >
                              > > Does any one know why this is happening and how i can fix it?
                              > >
                              > > cheers
                              > > Tim Henderson[/color][/color]

                              Comment

                              Working...