Tuples and lists

HI Everyone,
Just learning to code and have some questions that I dont understand - any help appreciated:

Q1)
CAn someone explain why the following has an error on line 2 and no error on line 3 when trying to amend a list in a tuple:

1 x = ([1], [2], [3])
2 x[0] = 4 # Error
3 x[0][0] = 4 # No error

Q2)
I have letters which is a list of ten letters. Which one of the following pieces of code could I use to change the first two elements to ‘a’ and ‘b’?

letters[0:1] = [‘a’, ‘b’]
letters[0:1] = (‘a’, ‘b’)
letters[0] = ‘a’
letters[1] = ‘b’

Many thanks

Hi Dan,

One question per message please! (Or at least make sure they’re
related.)

Tuples are immutable, so you cannot change them directly. So this raises
an error:

x = ([1], [2], [3])  # x is a tuple
x[0] = 4  # change the tuple by putting 4 into the zeroeth position

But lists aren’t immutable. It’s perfectly fine to change a list:

x = ([1], [2], [3])	 # x is	a tuple
y = x[0]  # y is the list in the zeroeth position of x
y[0] = 4  # changing the list is fine

But we don’t need the temporary name “y” there. We can eliminate it:

x = ([1], [2], [3])	 # x is	a tuple
x[0][0] = 4  # changing the list is fine

x[0] returns the list, and the second subscript ...[0] references
the zeroeth item in that list.

We’re not changing the tuple directly, we’re changing the list which
happens to be inside the tuple.

Hi again Dan, question two about lists. You asked:

“I have letters which is a list of ten letters. Which one of the
following pieces of code could I use to change the first two elements
to ‘a’ and ‘b’?”

Have you tried them to see which will work? Don’t be scared to try it.
Open the Python interactive interpreter. You should see a “>>>” prompt.
If you need help getting to that, please ask, we’ll be happy to help.

Once you see that prompt, do an experiment:

>>> letters = list('abcdefghij')
>>> print(letters)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
>>> letters[0:1] = ['x', 'y']
>>> print(letters)
['x', 'y', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

Hmmm, interesting. The slice letters[0:1] didn’t replace the first
two items, it replaced the first and inserted a second. What’s going on?

The indexes in a slice represent the spaces between items. So let’s
draw up a picture of what our list looked like, and mark in the slice
positions:

0 1 2 3 4 5 6 7 8 9 10
>a>b>c>d>e>f>g>h>i>j>

So the slice letters[0:1] begins at position 0 and ends at position 1.
There is only one item between those two positions, ‘a’, so the ‘a’ gets
replaced by two items, ‘x’ and ‘y’.

So to replace the ‘a’ and the ‘b’, our slice needs to start at 0 and go
to 2, not 1. Let’s try it:

>>> letters = list('abcdefghij')
>>> letters[0:2] = ['x', 'y']
>>> print(letters)
['x', 'y', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

Success!

The replacement items can be in any iterable object, a list, a tuple,
even a string. So letters[0:2] = ('x', 'y') would also work. So would
letters[0:2] = 'xy'.

Changing each item individually would also work:

letters[0] = 'x'
letters[1] = 'y'

but will be slower than using a slice.

Apologies Steven, will make it one question per post in future :slight_smile:

So then is it fair to say that you can’t change which items are in the tuple, so you can’t replace the first item by 4. BUT you can change an item internally, if it is mutable. So you can replace the first element of the first item by 4?

Cheers
Dan

Hi Steven,

Yes I tried the examples you showed below in python and achieved the same results.
My letters[0:1] = [‘a’, ‘b’] gave me a list of 11 as 2 were inserted instead of the original 1 character.

I tried this and this also netted the same result and inserted 2 characters in place of the first - letters[0:1] = (‘a’,‘b’)

Changing individually also worked and did not add an extra character, just changed the first 2 elements.

So does that mean all three methods work?

Cheers
Dan

Correct.

You cannot change the tuple directly. But the individual objects
inside the tuple don’t know that they are in a tuple. If they happen to
be mutable objects (lists, or dicts, for example) then you can modify
them to your heart’s content, regardless of whether they are inside a
tuple or not.

Hi Dan,

If by “work” you mean “don’t raise an exception”, then yes, they work.

But the two things you tried do different things:

  • Direct item assignment, one element at a time, replaces those items
    (but relatively slowly).

    letters[0] = ‘a’
    letters[1] = ‘b’

  • Slice assignment is faster, but the way you wrote it gives a different
    result:

    letters[0:1] = ‘ab’ # can also use a list [‘a’, ‘b’] or tuple

So if by “work” you mean “do the correct thing”, then only one of the
two is correct, but I don’t know which because I don’t know which
behaviour you wanted.

Remember, the hard part of programming is not avoiding exceptions.
That’s easy! This program will never raise an exception:

pass  # do nothing

The hard part is getting the program to do what you want, correctly.

We can slightly modify the slice version so that it replaces the first
two elements:

letters[0:2] = 'ab'

That will now match the first example using direct position by position
assigment.

Of course slicing is completely general:

letters[3:7] = ['x', 'y']

will replace the four items at positions 3, 4, 5 and 6 (but not 7)
with two items ‘x’, ‘y’. Remember that in slices, the start index
is included, and the end index is not.