Data sticking around too long

C

CedricCicada

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.scanList)) + " objects."

class Scanner:
def Read(self, data):
command = ScannerCommand()
command.scanList.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.scanList)
############## 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
 
S

skip

Cedric> Why is the ScannerCommand object being created with a scanList
Cedric> that contains the data that was in the previously created
Cedric> ScannerCommand object?

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

Skip
 
M

Matimus

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:
.... def __init__(self):
.... self.taskName = ''
.... self.scanList = []
.... print "Creating a ScannerCommand object; list has " + \
.... str(len(self.scanList)) + " objects."


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.scanList)) + " objects."

class Scanner:
def Read(self, data):
command = ScannerCommand()
command.scanList.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.scanList)
############## 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
 
C

CedricCicada

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.append(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
 
L

Larry Bates

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.append(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
 
C

CedricCicada

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.append(data)

if __name__ == '__main__':
c1 = ScannerCommand("c1")
c2 = ScannerCommand("c2")
c1.number = 1
c2.number = 2
c1.scanList.append("One")
c2.scanList.append("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
 
S

skip

Cedric> But I'd like to understand this a bit more.

Always a good idea. ;-)

Cedric> Here's another script:

Cedric> class ScannerCommand:
Cedric> taskName = ''
Cedric> scanList = []

Cedric> def __init__(self, data):
Cedric> self.scanList = []
Cedric> self.scanList.append(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
 
M

Matimus

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:
True
### j and i are attributes that point to the same instanceFalse
### assignment to I made a new instance and j and i are no longer the
same instance
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

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.append(data)

if __name__ == '__main__':
c1 = ScannerCommand("c1")
c2 = ScannerCommand("c2")
c1.number = 1
c2.number = 2
c1.scanList.append("One")
c2.scanList.append("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
 
S

Stefan Schwarzer

Hello Matimus,

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.append(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.append("One")
c2.scanList.append("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
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top