Data sticking around too long

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

    #1

    Data sticking around too long

    Greetings!

    Here's my script:

    ######## start of script
    class ScannerCommand:
    taskName = ''
    scanList = []

    def __init__(self):
    print "Creating a ScannerCommand object; list has " + \
    str(len(self.sc anList)) + " objects."

    class Scanner:
    def Read(self, data):
    command = ScannerCommand( )
    command.scanLis t.append(data)
    return command

    class Handler:
    def Process(self, dataList):
    print "Processing data"
    for data in dataList:
    print " " + data
    print "Finished processing data."

    if __name__ == '__main__':
    s = Scanner()
    count = 0
    for data in ["Zero", "One", "Two", "Three"]:
    command = s.Read(data)
    handler = Handler()
    handler.Process (command.scanLi st)
    ############## End of script

    Here's the result:

    ########## Start of result
    Processing data
    Zero
    Finished processing data.
    Creating a ScannerCommand object; list has 1 objects.
    Processing data
    Zero
    One
    Finished processing data.
    Creating a ScannerCommand object; list has 2 objects.
    Processing data
    Zero
    One
    Two
    Finished processing data.
    Creating a ScannerCommand object; list has 3 objects.
    Processing data
    Zero
    One
    Two
    Three
    Finished processing data.
    ############### # End of result

    I had expected to get "Zero" by itself, then "One" by itself, and so
    on.

    Why is the ScannerCommand object being created with a scanList that
    contains the data that was in the previously created ScannerCommand
    object? And what do I have to do to ensure that the code that is run
    when I call ScannerCommand( ) gives me an object with an empty scanList?

    I am a C++ developer in a shop that uses some Python as well, and I
    just ran across this behavior for the first time. I believe it is due
    to the difference between binding a variable to an object, as Python
    does, and actually creating the variable, as a similar C++ application
    would do.

    Thank you very much.

    Rob Richardson
    RAD-CON, Inc.
    Bay Village, OH

  • skip@pobox.com

    #2
    Re: Data sticking around too long


    CedricWhy is the ScannerCommand object being created with a scanList
    Cedricthat contains the data that was in the previously created
    CedricScannerCo mmand object?

    Your scanList attribute is defined at the class level and is thus shared by
    all ScannerCommand instances.

    Skip

    Comment

    • Matimus

      #3
      Re: Data sticking around too long

      taskName and scanList are defined at the class level making them class
      attributes. Each instance of the ScannerCommand class will share its
      class attributes. What you want are instance attributes which can be
      initialized whithin the constructor like so:
      >>class ScannerCommand:
      .... def __init__(self):
      .... self.taskName = ''
      .... self.scanList = []
      .... print "Creating a ScannerCommand object; list has " + \
      .... str(len(self.sc anList)) + " objects."


      CedricCicada@gm ail.com wrote:
      Greetings!
      >
      Here's my script:
      >
      ######## start of script
      class ScannerCommand:
      taskName = ''
      scanList = []
      >
      def __init__(self):
      print "Creating a ScannerCommand object; list has " + \
      str(len(self.sc anList)) + " objects."
      >
      class Scanner:
      def Read(self, data):
      command = ScannerCommand( )
      command.scanLis t.append(data)
      return command
      >
      class Handler:
      def Process(self, dataList):
      print "Processing data"
      for data in dataList:
      print " " + data
      print "Finished processing data."
      >
      if __name__ == '__main__':
      s = Scanner()
      count = 0
      for data in ["Zero", "One", "Two", "Three"]:
      command = s.Read(data)
      handler = Handler()
      handler.Process (command.scanLi st)
      ############## End of script
      >
      Here's the result:
      >
      ########## Start of result
      Processing data
      Zero
      Finished processing data.
      Creating a ScannerCommand object; list has 1 objects.
      Processing data
      Zero
      One
      Finished processing data.
      Creating a ScannerCommand object; list has 2 objects.
      Processing data
      Zero
      One
      Two
      Finished processing data.
      Creating a ScannerCommand object; list has 3 objects.
      Processing data
      Zero
      One
      Two
      Three
      Finished processing data.
      ############### # End of result
      >
      I had expected to get "Zero" by itself, then "One" by itself, and so
      on.
      >
      Why is the ScannerCommand object being created with a scanList that
      contains the data that was in the previously created ScannerCommand
      object? And what do I have to do to ensure that the code that is run
      when I call ScannerCommand( ) gives me an object with an empty scanList?
      >
      I am a C++ developer in a shop that uses some Python as well, and I
      just ran across this behavior for the first time. I believe it is due
      to the difference between binding a variable to an object, as Python
      does, and actually creating the variable, as a similar C++ application
      would do.
      >
      Thank you very much.
      >
      Rob Richardson
      RAD-CON, Inc.
      Bay Village, OH

      Comment

      • CedricCicada@gmail.com

        #4
        Re: Data sticking around too long

        Skip and Matimus,

        Thank you for your replies. Putting initialization in the constructor
        gets me what I want. But I'd like to understand this a bit more.
        Here's another script:

        class ScannerCommand:
        taskName = ''
        scanList = []

        def __init__(self, data):
        self.scanList = []
        self.scanList.a ppend(data)

        if __name__ == '__main__':
        c1 = ScannerCommand( "c1")
        c2 = ScannerCommand( "c2")
        print "C1: "
        for data in c1.scanList:
        print " " + data
        print "C2: "
        for data in c2.scanList:
        print " " + data

        And here's the output, which is what I want:
        C1:
        c1
        C2:
        c2

        If scanList is a class variable shared between all instances of the
        class, then C1's list should have held "C2" when I printed it, since C2
        came along and changed scanList. But obviously, here it's not a class
        variable and the two instances have their own lists.

        If I don't initialize scanList in the constructor, then scanList is a
        class variable (in C++, it would be a static member of the class) that
        is shared among all instances of the class. If I do initialize
        scanList in the constructor, then scanList magically becomes an
        instance variable, with every instance of the ScannerCommand object
        having its own scanList list??? Is that really the way it works? I
        would have thought the C++ way, with some special syntax to distinguish
        a class variable from an instance variable, would be much easier to
        work with.

        Thanks again!

        Rob Richardson
        RAD-CON, Inc.
        Bay Village, OH

        Comment

        • Larry Bates

          #5
          Re: Data sticking around too long

          CedricCicada@gm ail.com wrote:
          Skip and Matimus,
          >
          Thank you for your replies. Putting initialization in the constructor
          gets me what I want. But I'd like to understand this a bit more.
          Here's another script:
          >
          class ScannerCommand:
          taskName = ''
          scanList = []
          >
          def __init__(self, data):
          self.scanList = []
          self.scanList.a ppend(data)
          >
          if __name__ == '__main__':
          c1 = ScannerCommand( "c1")
          c2 = ScannerCommand( "c2")
          print "C1: "
          for data in c1.scanList:
          print " " + data
          print "C2: "
          for data in c2.scanList:
          print " " + data
          >
          And here's the output, which is what I want:
          C1:
          c1
          C2:
          c2
          >
          If scanList is a class variable shared between all instances of the
          class, then C1's list should have held "C2" when I printed it, since C2
          came along and changed scanList. But obviously, here it's not a class
          variable and the two instances have their own lists.
          >
          If I don't initialize scanList in the constructor, then scanList is a
          class variable (in C++, it would be a static member of the class) that
          is shared among all instances of the class. If I do initialize
          scanList in the constructor, then scanList magically becomes an
          instance variable, with every instance of the ScannerCommand object
          having its own scanList list??? Is that really the way it works? I
          would have thought the C++ way, with some special syntax to distinguish
          a class variable from an instance variable, would be much easier to
          work with.
          >
          Thanks again!
          >
          Rob Richardson
          RAD-CON, Inc.
          Bay Village, OH
          >
          You shadowed the class attribute scanList by creating an instance
          variable called self.scanList in the __init__ method:

          self.scanList = []

          Comment that line out and see what you get.

          -Larry Bates

          Comment

          • CedricCicada@gmail.com

            #6
            Re: Data sticking around too long

            Greetings again!

            There's something more to determining whether a class member is a class
            variable or an instance variable. Here's a slightly expanded version
            of my last script:

            class ScannerCommand:
            taskName = ''
            scanList = []
            number = 0

            def __init__(self, data):
            pass
            # self.scanList = []
            # self.scanList.a ppend(data)

            if __name__ == '__main__':
            c1 = ScannerCommand( "c1")
            c2 = ScannerCommand( "c2")
            c1.number = 1
            c2.number = 2
            c1.scanList.app end("One")
            c2.scanList.app end("Two")
            print "C1: " + str(c1.number)
            for data in c1.scanList:
            print " " + data
            print "C2: " + str(c2.number)
            for data in c2.scanList:
            print " " + data

            And here's the output:
            C1: 1
            One
            Two
            C2: 2
            One
            Two

            Here, the two instances share the scanList list, as expected from
            previous discussions, but they have their own copies of the number
            variable. Why is scanList a class variable but number an instance
            variable here?

            Good night, all!

            Rob Richardson
            RAD-CON, Inc.
            Bay Village, OH

            Comment

            • skip@pobox.com

              #7
              Re: Data sticking around too long

              CedricBut I'd like to understand this a bit more.

              Always a good idea. ;-)

              CedricHere's another script:

              Cedricclass ScannerCommand:
              Cedric taskName = ''
              Cedric scanList = []

              Cedric def __init__(self, data):
              Cedric self.scanList = []
              Cedric self.scanList.a ppend(data)
              ...

              The assignment to self.scanList in __init__ creates an instance attribute in
              the ScannerCommand instance. The scanList attribute at the class level is
              available as ScannerCommand. scanList. In your previous code, (no assignment
              to self.scanList in __init__), references to self.scanList would search
              first in the instance dict for a "scanList" key, then failing to find
              anything there, search the class's dict and find its "scanList" key.

              Skip

              Comment

              • Matimus

                #8
                Re: Data sticking around too long

                Someone correct me if I'm wrong (sometimes I get the terms mixed up)
                but I believe that what you are seeing is due to 'number' being an
                immutable type. This means that its value cannot be changed and thus
                each assignment is effectively creating a new instance if int. I
                believe lists are considered immutable also but since it is a container
                type it behaves somewhat differently. I read a thread earlier that
                addressed a similar issue but I can't find it now. I will post again if
                I come accross it. Anyway, as a demonstration try this using python's
                command line prompt:
                >>i = int()
                >>j = i
                >>j is i
                True
                ### j and i are attributes that point to the same instance
                >>i = 1
                >>j is i
                False
                ### assignment to I made a new instance and j and i are no longer the
                same instance
                >>a = [0,1,2]
                >>b = a
                >>b is a
                True
                ### the same is true for lists
                >>b = [0,1,3]
                >>b is a
                False
                >>b = a
                >>b is a
                True
                ### but since 'a' is a container its contents can be modified without
                creating a new instance
                >>b[1] = 3
                >>b is a
                True
                >>a
                [0,3,2]
                >>b
                [0,3,2]
                >>a.append(4)
                >>a
                [0,3,2,4]
                >>b
                [0,3,2,4]
                >>b is a
                True

                I hope this helps.

                -Matt

                CedricCicada@gm ail.com wrote:
                Greetings again!
                >
                There's something more to determining whether a class member is a class
                variable or an instance variable. Here's a slightly expanded version
                of my last script:
                >
                class ScannerCommand:
                taskName = ''
                scanList = []
                number = 0
                >
                def __init__(self, data):
                pass
                # self.scanList = []
                # self.scanList.a ppend(data)
                >
                if __name__ == '__main__':
                c1 = ScannerCommand( "c1")
                c2 = ScannerCommand( "c2")
                c1.number = 1
                c2.number = 2
                c1.scanList.app end("One")
                c2.scanList.app end("Two")
                print "C1: " + str(c1.number)
                for data in c1.scanList:
                print " " + data
                print "C2: " + str(c2.number)
                for data in c2.scanList:
                print " " + data
                >
                And here's the output:
                C1: 1
                One
                Two
                C2: 2
                One
                Two
                >
                Here, the two instances share the scanList list, as expected from
                previous discussions, but they have their own copies of the number
                variable. Why is scanList a class variable but number an instance
                variable here?
                >
                Good night, all!
                >
                Rob Richardson
                RAD-CON, Inc.
                Bay Village, OH

                Comment

                • Stefan Schwarzer

                  #9
                  Re: Data sticking around too long

                  Hello Matimus,

                  On 2006-09-07 00:07, Matimus wrote:
                  Someone correct me if I'm wrong (sometimes I get the terms mixed up)
                  but I believe that what you are seeing is due to 'number' being an
                  immutable type. This means that its value cannot be changed and thus
                  each assignment is effectively creating a new instance if int.
                  The significant difference is not between mutable and immutable
                  objects, but between modifying an object in-place
                  (e. g. some_list.appen d(1)) and assigning an object to a name
                  (e. g. some_int = 1). If you do an assignment to a class
                  "variable", the binding to the previous object (if any) will be
                  removed and a new binding to the assigned object created.

                  The assignments Rob does with

                  c1.number = 1
                  c2.number = 2

                  create a new name "number" in each of the instances' namespaces
                  and assign the objects 1 and 2 to them, respectively. In contrast,
                  the list scanList is modified in-place with

                  c1.scanList.app end("One")
                  c2.scanList.app end("Two")

                  so no names are created in the instances' namespaces and both
                  operations modify the object in the class's namespace.
                  I believe lists are considered immutable also
                  I guess you confuse this with tuples.
                  but since it is a container
                  type it behaves somewhat differently.
                  Again, this has nothing per se to do with being a container
                  though many containers are mutable objects and often are
                  modified in-place.

                  Stefan

                  Comment

                  Working...