constructor list slice confusion

S

Simon Morgan

Hi,

Can somebody please explain to me why:

class SomeClass:
def __init__(self, contents=[]):
self.contents = contents[:]
def add(self, element):
self.contents.append(element)

when called a second time (i.e. to create a new instance of a SomeClass
object) results in self.contents being assigned an empty list when say for
example I've done:

foo = SomeClass()
foo.add(1)
foo.add(2)

beforehand?

I understand that the default value is only evaluated once and therefore
retains a reference to the same list which is why:

self.contents = contents

results in objects sharing the same contents, but I was under the
impression that [:] basically produced a copy of an entire list. So
in that case wouldn't blah.contents contain a copy of foo.contents when
it's created given that no other list is specified as a parameter to
__init__?

I've read a couple of explanations which were rather vague so I'm having
trouble grokking what's going on.

Thanks.
 
P

Peter Otten

Simon said:
Can somebody please explain to me why:

class SomeClass:
def __init__(self, contents=[]):
self.contents = contents[:]
def add(self, element):
self.contents.append(element)

when called a second time (i.e. to create a new instance of a SomeClass
object) results in self.contents being assigned an empty list when say for
example I've done:

foo = SomeClass()
foo.add(1)
foo.add(2)

beforehand?

Maybe, after a little renaming you can see it yourself:

class SomeClass:
def __init__(self, default_contents=[]):
# make a copy of default_contents that is
# kept in a SomeClass instance
self.contents = default_contents[:]
def add(self, element):
# modify the *copy* of default_contents...
self.contents.append(element)

When you want modifiable defaults, you can use a class attribute:
.... default_contents = []
.... def __init__(self, contents=default_contents):
.... self.contents = contents[:]
.... def add(self, elem): self.contents.append(elem)
.... def addDefault(self, elem): self.default_contents.append(elem)
.... def __repr__(self):
.... return "SomeClass(contents=%s, default_contents=%s)" % (
.... self.contents, self.default_contents)
....
foo = SomeClass()
foo SomeClass(contents=[], default_contents=[])
foo.add(1)
foo.addDefault("x")
foo SomeClass(contents=[1], default_contents=['x'])

bar = SomeClass()
bar SomeClass(contents=['x'], default_contents=['x'])
bar.add(2)
bar
SomeClass(contents=['x', 2], default_contents=['x'])

Peter
 
S

Simon Morgan

Maybe, after a little renaming you can see it yourself:

class SomeClass:
def __init__(self, default_contents=[]):
# make a copy of default_contents that is # kept in a SomeClass
instance
self.contents = default_contents[:]
def add(self, element):
# modify the *copy* of default_contents...
self.contents.append(element)

Aha, I think I get it now. default_contents will always be an empty list
because before anything is added to it, a copy is made.

I think I was focusing too hard on the constructor. :)

Thanks for your help.
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top