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

putvande
  • 15,068
  • 3
  • 34
  • 50
  • 1
    If you're coming from a language where assignment creates copies, [this](http://rg03.wordpress.com/2007/04/21/semantics-of-python-variable-names-from-a-c-perspective/) may help. – user2357112 Aug 15 '13 at 23:09
  • 1
    Also: http://nedbatchelder.com/text/names.html –  Aug 15 '13 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. – Neil Aug 15 '13 at 23:12
  • @user2357112 is there a way to create a real new list? –  Aug 15 '13 at 23:12
  • @neil they're just above the code. samples_in = [] samples = [] –  Aug 15 '13 at 23:13
  • 1
    @mito_562condrio: `samples_in = []` replaces whatever samples_in was with an empty list. – Neil Aug 15 '13 at 23:13
  • @neil but they're above the code –  Aug 15 '13 at 23:14
  • @mito_562condrio: No, he's saying that `samples_in = []` is what you'd use to make a new `samples_in` list, rather than deleting the contents of the old one. He was responding to the question you asked me. – user2357112 Aug 15 '13 at 23:15
  • @neil Thank you. It worked, but now at the end of the code I get Unexpected error: –  Aug 15 '13 at 23:16
  • @mito_562condrio: Your new error seems to be entirely unrelated to your old one—and also entirely unrelated to any of the code you've shown or described so far. You should create a new question, and show the code that causes that error (ideally a [SSCCE](http://sscce.org) that has _just enough_ of your code to be runnable up to that error). Meanwhile, if one of the answers to this question solved the problem for you, accept it; if not, explain what you think should be in an answer (or just write one yourself). (And if answers were helpful to you but not complete solutions, upvote them.) – abarnert Aug 15 '13 at 23:58

3 Answers3

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

beetea
  • 308
  • 1
  • 8
  • 1
    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… – abarnert Aug 16 '13 at 00:10
  • 2
    +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 Aug 16 '13 at 07:34
  • @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). – abarnert Aug 16 '13 at 17:37
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)
abarnert
  • 354,177
  • 51
  • 601
  • 671
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.

Mark R. Wilkins
  • 1,282
  • 7
  • 15