2

Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument

I'm trying to create a list of objects from the class "fooclass", with different attributes, but always end up with all elements of the list containing the same values.

Here is the code I run:

#!/usr/bin/env python

class fooclass():
  def __init__(self,vertices = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]):
    self.vertices = vertices

l=[]
print(l)
a=fooclass(); a.vertices[0]=[7,9,9]; l.append(a)
print 'a=', a.vertices
a=fooclass(); a.vertices[0]=[789,9,9]; l.append(a)
print 'a=', a.vertices
print(l[0].vertices)
print(l[1].vertices)
print(l)

l=[]
print(l)
a=fooclass(); a.vertices[0]=[7,9,9]; l.append(a)
print 'a=', a.vertices
b=fooclass(); b.vertices[0]=[789,9,9]; l.append(b)
print 'b=', b.vertices
print(l[0].vertices)
print(l[1].vertices)
print(l[0])
print(l[1])

And the output I get:

$  python ./class_test2.py
[]
a= [[7, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
a= [[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[<__main__.fooclass instance at 0x7f945eafecf8>, <__main__.fooclass instance at 0x7f945eafed88>]
[]
a= [[7, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
b= [[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[789, 9, 9], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
<__main__.fooclass instance at 0x7f945eafecf8>
<__main__.fooclass instance at 0x7f945eafed88>

Why are l[0].vertices and l[1].vertices exactly the same despite inputting different values?

System info:

Ubuntu 10.04.4 LTS
$  python --version
Python 2.6.5
$  uname -a
Linux *** 2.6.32-39-generic #86-Ubuntu SMP Mon Feb 13 21:50:08 UTC 2012 x86_64 GNU/Linux

Note: Tried with Python 3.1.2 (just changing the print statements), same problem. :(

Community
  • 1
  • 1
KIAaze
  • 340
  • 1
  • 13
  • 5
    Sigh, the [mutable default argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) again... – Sven Marnach Mar 20 '12 at 17:12
  • Also, inherit your class from "object" or be subject to nasty elusive behaviors when you need more advanced class features. (Classes that do not inherit from object are "old-style" classes in Python 2, and don't support all available features) – jsbueno Mar 20 '12 at 17:40
  • Thanks for the warning. Just did that for all my parent classes ( since it doesn't seem to work for child classes, which inherit from the parents I suppose). – KIAaze Mar 20 '12 at 17:56

2 Answers2

6

The default value of vertices can be modified in Python. You can rewrite __init__ like so to avoid it:

def __init__(self, vertices=None):
    if vertices is None:
        vertices = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
    self.vertices = vertices
Community
  • 1
  • 1
phihag
  • 278,196
  • 72
  • 453
  • 469
  • Thanks. It makes the classes a lot longer (because I have a lot of default arguments), but at least it works. – KIAaze Mar 20 '12 at 17:46
2

When you set the default argument vertices = [...], you're actually instantiating a single list object to be used every time the argument is unspecified. That means both of your fooclass instances are sharing--and modifying--a single list.

Instead, instantiate it inside your __init__ method if no value is given for that argument. That ensures that a new one is created each time the method is run.

Edit: phihag's answer gives good example code for correcting __init__.

Etaoin
  • 8,444
  • 2
  • 28
  • 44