I searched around and found this article which gives me a lead on why this is happening -- it seems I've just come across a more complex and subtle case of it. But it's those subtleties that have me bewildered and looking for a clearer explanation. (I doubt it matters, but just in case: I'm using Python 2.7.x on Windows 7.)
The starting point is this bit of code, which I distilled from a much larger and more complex app where I first encountered the problem.
class MyClass():
def printStuff(self,myList=[1,2,3],myInt=4):
print '--------------------------------'
print 'myList at start: '+str(myList)
print 'myInt at start: '+str(myInt)
doNotCare = myList.pop()
myInt = myInt - 1
print 'myList at end: '+str(myList)
print 'myInt at end: '+str(myInt)
testMC = MyClass()
testMC.printStuff()
testMC.printStuff()
testMC.printStuff()
...which generates the following output:
--------------------------------
myList at start: [1, 2, 3]
myInt at start: 4
myList at end: [1, 2]
myInt at end: 3
--------------------------------
myList at start: [1, 2]
myInt at start: 4
myList at end: [1]
myInt at end: 3
--------------------------------
myList at start: [1]
myInt at start: 4
myList at end: []
myInt at end: 3
Now, based on the various write-ups on how defaulted parameters are handled in functions, it seems like myInt
should exhibit the same behavior as myList
, i.e. it should decrement to 3, 2, 1. But obviously it doesn't.
The puzzle gets more complex if I modify the doNotCare = myList.pop()
line, instead using a slice to update the list:
class MyClass():
def printStuff(self,myList=[1,2,3],myInt=4):
print '--------------------------------'
print 'myList at start: '+str(myList)
print 'myInt at start: '+str(myInt)
myList = myList[:-1]
myInt = myInt - 1
print 'myList at end: '+str(myList)
print 'myInt at end: '+str(myInt)
testMC = MyClass()
testMC.printStuff()
testMC.printStuff()
testMC.printStuff()
...which somehow defeats that unexpected retained value, yielding the output:
--------------------------------
myList at start: [1, 2, 3]
myInt at start: 4
myList at end: [1, 2]
myInt at end: 3
--------------------------------
myList at start: [1, 2, 3]
myInt at start: 4
myList at end: [1, 2]
myInt at end: 3
--------------------------------
myList at start: [1, 2, 3]
myInt at start: 4
myList at end: [1, 2]
myInt at end: 3
So I suppose the first question would be, is this in fact just a more complex and subtle case of the default-parameter behavior described in the reference? If it is, then why doesn't the behavior apply to myInt
-- or even to myList
, when I manipulate it with slicing instead of pop
?