user1952219
user1952219

Reputation:

Weird list behavior in python

qtd_packs = 2
size_pack = 16
pasta = []
pasta.append ('packs/krun/')
pasta.append ('packs/parting2/')

for k in range(0, qtd_packs):
    for n in range(1, size_pack+1):
        samples_in.append (pasta[k]+str(n)+'.wav')
    samples.append(samples_in)
    del samples_in[0:len(samples_in)]


print(samples)

I'm basically trying to add the samples_in inside the samples list, then delete the old samples_in list to create a new one. This will happen 2 times, as the qtd_packs =2. But in the end, what I get is two empty lists:

[[], []]

I've append'ed the samples_in inside samples BEFORE deleting it. So what happened?

Thank you

Upvotes: 1

Views: 758

Answers (3)

abarnert
abarnert

Reputation: 366103

The problem is that after this:

samples.append(samples_in)

The newly-appended value in samples is not a copy of samples_in, it's the exact same value. You can see this from the interactive interpreter:

>>> samples_in = [0]
>>> samples = []
>>> samples.append(samples_in)
>>> samples[-1] is samples_in
True
>>> id(samples[-1]), id(samples_in)
(12345678, 12345678)

Using an interactive visualizer might make it even easier to see what's happening.


So, when you modify the value through one name, like this:

>>> del samples_in[0:len(samples_in)]

The same modification is visible through both names:

>>> samples[-1]
[]

Once you realize that both names refer to the same value, that should be obvious.


As a side note, del samples_in[:] would do the exact same thing as del samples_in[0:len(samples_in)], because those are already the defaults for a slice.


What if you don't want the two names to refer to the same value? Then you have to explicitly make a copy.

The copy module has functions that can make a copy of (almost) anything, but many types have a simpler way to do it. For example, samples_in[:] asks for a new list, which copies the slice from 0 to the end (again, those are the defaults). So, if you'd done this:

>>> samples.append(samples_in[:])

… you would have a new value in samples[-1]. Again, you can test that easily:

>>> samples[-1], samples_in
([0], [0])
>>> samples[-1] == samples_in
True
>>> samples[-1] is samples_in
False
>>> id(samples[-1]), id(samples_in)
23456789, 12345678

And if you change one value, that doesn't affect the other—after all, they're separate values:

>>> del samples_in[:]
>>> samples[-1], samples_in
([0], [])

However, in this case, you really don't even need to make a copy. The only reason you're having a problem is that you're trying to reuse samples_in over and over. There's no reason to do that, and if you just created a new samples_in value each time, the problem wouldn't have come up in the first place. Instead of this:

samples_in = []
for k in range(0, qtd_packs):
    for n in range(1, size_pack+1):
        samples_in.append (pasta[k]+str(n)+'.wav')
    samples.append(samples_in)
    del samples_in[0:len(samples_in)]

Do this:

for k in range(0, qtd_packs):
    samples_in = []
    for n in range(1, size_pack+1):
        samples_in.append (pasta[k]+str(n)+'.wav')
    samples.append(samples_in)

Upvotes: 1

Mark R. Wilkins
Mark R. Wilkins

Reputation: 1302

beetea's answer below offers the solution if you want samples to contain two lists, each of which have the strings for one of your two qtd_packs:

qtd_packs = 2
size_pack = 16

pasta = []
pasta.append ('packs/krun/')
pasta.append ('packs/parting2/')

samples = []
samples_in = []
for k in range(0, qtd_packs):
    for n in range(1, size_pack+1):
        samples_in.append (pasta[k]+str(n)+'.wav')
    samples.append(samples_in[:])
    del samples_in[0:len(samples_in)]

print(samples)

produces this output:

[['packs/krun/1.wav', 'packs/krun/2.wav', 'packs/krun/3.wav', 'packs/krun/4.wav',
'packs/krun/5.wav', 'packs/krun/6.wav', 'packs/krun/7.wav', 'packs/krun/8.wav',
'packs/krun/9.wav', 'packs/krun/10.wav', 'packs/krun/11.wav', 'packs/krun/12.wav',
'packs/krun/13.wav', 'packs/krun/14.wav', 'packs/krun/15.wav', 'packs/krun/16.wav'],
['packs/parting2/1.wav', 'packs/parting2/2.wav', 'packs/parting2/3.wav',
'packs/parting2/4.wav', 'packs/parting2/5.wav', 'packs/parting2/6.wav',
'packs/parting2/7.wav', 'packs/parting2/8.wav', 'packs/parting2/9.wav',
'packs/parting2/10.wav', 'packs/parting2/11.wav', 'packs/parting2/12.wav',
'packs/parting2/13.wav', 'packs/parting2/14.wav', 'packs/parting2/15.wav',
'packs/parting2/16.wav']]

Now, when I originally read your question, I thought you were trying to make a single list containing all the strings. In that instance, you could use

samples.extend(samples_in)

instead of

samples.append(samples_in[:])

and you would get a flat list containing only the strings.

Upvotes: 0

beetea
beetea

Reputation: 308

In Python, lists are passed by reference. When you append samples_in to samples, Python appends a reference to samples_in to samples. If you want to append a copy of samples_in to samples, you can do:

samples.append(samples_in[:])

This effectively creates a new list from all the items in samples_in and passes that new list into samples.append(). So now when you clear the items in samples_in, you're not clearing the items in the list that was appended to samples as well.

Also, note that samples_in[:] is equivalent to samples_in[0:len(samples_in)].

Upvotes: 2

Related Questions