List weirdness - what the heck is going on here?

R

Rotwang

Hi all, I've been trying to make a class with which to manipulate sound
data, and have run into some behaviour I don't understand which I hope
somebody here can explain. The class has an attribute called data, which
is a list with two elements, one for each audio channel, each of which
is a list containing the audio data for that channel. It also has
various methods to write data such as sine waves and so on, and a method
to insert data from one sound at the start of data from another.
Schematically, the relevant bits look like this:

class sound:
def f(self):
self.data = [[0]]*2

def insert(self, other):
for c in xrange(2):
self.data[c][0:0] = other.data[c]

However, the insert method doesn't work properly; x.insert(y) adds two
copies of y's data to the start of x's data, instead of one. From a
session in IDLE:
>>> x = sound()
>>> y = sound()
>>> x.f()
>>> y.f()
>>> x.data [[0], [0]]
>>> x.insert(y)
>>> x.data
[[0, 0, 0], [0, 0, 0]]

But suppose I replace the line

self.data = [[0]]*2

with

self.data = [[0] for c in xrange(2)]

Then it works fine:
>>> x = sound()
>>> y = sound()
>>> x.f()
>>> y.f()
>>> x.data [[0], [0]]
>>> x.insert(y)
>>> x.data
[[0, 0], [0, 0]]

Can anybody tell me what's going on?
 
O

Owen Jacobson

Hi all, I've been trying to make a class with which to manipulate sound
data, and have run into some behaviour I don't understand which I hope
somebody here can explain. The class has an attribute called data,
which is a list with two elements, one for each audio channel, each of
which is a list containing the audio data for that channel. It also has
various methods to write data such as sine waves and so on, and a
method to insert data from one sound at the start of data from another.
Schematically, the relevant bits look like this:

class sound:
def f(self):
self.data = [[0]]*2

Consider that this is equivalent to

def f(self):
x = [0]
self.data = [x, x]

self.data is now a list containing two references to the list
referenced by x -- so changes via either of the elements of self.data
will affect both elements. Your comprehension version creates a list
containing two distinct list objects, so this doesn't happen.
Can anybody tell me what's going on?

-o
 
A

alex23

Rotwang said:
Can anybody tell me what's going on?

Your problem is basically this:
a = [1]
b = [a] * 2
b [[1], [1]]
a.append(2)
b
[[1, 2], [1, 2]]

The expression '[a] * 2' doesn't make two copies of list of a list of
a, it makes two nested _references_ to it. When you modify the 'first'
list, you're seeing that changed reflected in the second reference.

The list comprehension, however, is returning a _new_ list each time.
Changing its contents doesn't affect any of the other independently
created lists.
 
A

Arnaud Delobelle

Rotwang said:
Hi all, I've been trying to make a class with which to manipulate
sound data, and have run into some behaviour I don't understand which
I hope somebody here can explain. The class has an attribute called
data, which is a list with two elements, one for each audio channel,
each of which is a list containing the audio data for that channel. It
also has various methods to write data such as sine waves and so on,
and a method to insert data from one sound at the start of data from
another. Schematically, the relevant bits look like this:

class sound:
def f(self):
self.data = [[0]]*2

def insert(self, other):
for c in xrange(2):
self.data[c][0:0] = other.data[c]

However, the insert method doesn't work properly; x.insert(y) adds two
copies of y's data to the start of x's data, instead of one. From a
session in IDLE:
x = sound()
y = sound()
x.f()
y.f()
x.data [[0], [0]]
x.insert(y)
x.data
[[0, 0, 0], [0, 0, 0]]

But suppose I replace the line

self.data = [[0]]*2

with

self.data = [[0] for c in xrange(2)]

Then it works fine:
x = sound()
y = sound()
x.f()
y.f()
x.data [[0], [0]]
x.insert(y)
x.data
[[0, 0], [0, 0]]

Can anybody tell me what's going on?

It's a FAQ!

http://www.python.org/doc/faq/programming/#how-do-i-create-a-multidimensional-list
 
R

Rotwang

Owen said:
Hi all, I've been trying to make a class with which to manipulate
sound data, and have run into some behaviour I don't understand which
I hope somebody here can explain. The class has an attribute called
data, which is a list with two elements, one for each audio channel,
each of which is a list containing the audio data for that channel. It
also has various methods to write data such as sine waves and so on,
and a method to insert data from one sound at the start of data from
another. Schematically, the relevant bits look like this:

class sound:
def f(self):
self.data = [[0]]*2

Consider that this is equivalent to

def f(self):
x = [0]
self.data = [x, x]

self.data is now a list containing two references to the list referenced
by x -- so changes via either of the elements of self.data will affect
both elements. Your comprehension version creates a list containing two
distinct list objects, so this doesn't happen.

Thanks, and likewise to everyone else who replied.
 

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

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top