0

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?

Community
  • 1
  • 1
JDM
  • 1,709
  • 3
  • 25
  • 48
  • 1
    [Mutable default argument.](http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments) – user2357112 Dec 31 '16 at 19:09

1 Answers1

1

Unlike lists, numbers are immutable, so they can't be changed. References to them can be reassigned, but a 1 will always be a 1.

And splicing doesn't produce this behavior because it creates a copy of the original, whereas pop modifies the original list.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Thanks, that makes sense of it. So tuples wouldn't be affected either -- only lists, dictionaries, etc. – JDM Dec 31 '16 at 20:27
  • 1
    @JDM Right. Anything immutable, tuples included, are "safe". You need to be careful with mutable objects though. – Carcigenicate Dec 31 '16 at 20:39