1
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

10
  • 1
    If you're coming from a language where assignment creates copies, this may help. Commented Aug 15, 2013 at 23:09
  • 1
    Also: nedbatchelder.com/text/names.html Commented Aug 15, 2013 at 23:10
  • Can you show the real code? You haven't shown where samples_in is created, so samples_in.append() will raise an exception, not give you the two empty lists you describe. Commented Aug 15, 2013 at 23:12
  • @user2357112 is there a way to create a real new list? Commented Aug 15, 2013 at 23:12
  • 1
    @mito_562condrio: samples_in = [] replaces whatever samples_in was with an empty list. Commented Aug 15, 2013 at 23:13

3 Answers 3

2

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)].

Sign up to request clarification or add additional context in comments.

3 Comments

Saying "lists are passed by reference" is very confusing. Python's calling semantics are indeed the same as what Ruby users call "pass by reference", but they're also the same as what Java and VB users call "pass by value". And really, they're both accurate, just looking at it from a different viewpoint. (Python passes by reference to values, not by reference to variables, or by value of variables…) People have tried to deal with this by inventing new terms, like "pass by sharing" or "pass by object", but most of the people you're explaining to won't know those terms…
+1 to abarnert. Technically, Python assignment means assigning the address of the target object -- the addres is technically the reference. Actually, the semantics of the variable may differ in Python when compared with other languages. A Python variable is an item in a dictionary where key is a string that captures the variable name, and the value is the reference to the target object. This way, it does not make sense to think about reference to a variable in Python. And this is the source of confusion when compared with other languages, in my opinion.
@pepr: Well, technically you're describing the CPython implementation, not the Python language; PyPy and Jython are written in languages (Python and Java, respectively) that don't even have the concept of addresses. But addresses are still definitely a useful way to think for people coming from C or C++. And the point about variables being just names that are (typically) keys in a dict is a great way to describe the big issue (and I think you're right that this issue is the main source of confusion for most people coming from other languages).
1

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)

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.