Change a list element by changing its slice element - python

Consider:
a = [1, 2, 3]
When I slice it I get a new list b:
b = a[:2]
If I now alter list b:
b[0] = 0
I get a = [1, 2, 3] and b = [0, 2]. Why doesn't the a list get altered as well? I know that slicing creates a new list object, but why is it not the object of the same elements as the inital object? How can I slice a so that:
a = [1, 2, 3]
b = a[:2]
b[0] = 0
Results in a = [0, 2, 3] and b = [0, 2].

You could try slicing later:
a = [1, 2, 3]
b = a
b[0] = 0
b = b[:2]
print(a, b)
Output:
[0, 2, 3] [0, 2]

The notation [:2] is making a shallow copy of the list a upto the 3rd index.
For instance, if you would have ever seen programmers doing [:] while iterating over a list, they are creating the shallow copy of the list. Similarly, the above notation makes a copy of the list upto the 3rd index so you get something as:
[1, 2]
To get the result, you can do:
a = [1, 2, 3]
b = a
b[0] =0
In here b = a is not making a copy the list. Both a, b point to the same list in the memory.
If you change either of the 2, it will reflect in both of them. Later, you can slice b by [:2]

Related

Why am I getting Type Error on list += but it works on list.append()

I'm picking up programming on python, am learning how to create basic caesar cypher and ran across the rather common
TypeError: 'int' object is not iterable
I've taken a look around and found out what the type error means but out of curiosity I tried if the code would work with list.append and surprisingly it did. Now I'm puzzled because I thought list.append and list += could be used similarly for adding to a list, below is part of the code that works when using list.append. What's the difference with how these two work or interpreted?
text_to_list = list(text)
list_to_number = []
for letter in text_to_list:
cipher_num = alphabet.index(letter)
list_to_number.append(cipher_num)
The main difference between += and list.append() is that += is used to extend a list by adding values from another list, while append adds a value to the end of the list.
Examples
a = [1, 2, 3]
a.append(3) # now a = [1, 2, 3, 3]
b = [1, 2, 3]
b += [3] # now b = [1, 2, 3, 3]
c = [1, 2, 3]
c += 3 # TypeError
d = [1, 2, 3]
d.append([3]) # now d = [1, 2, 3, [3]]
There is a built in list method, .extend(), which behaves the same as +=.
e = [1, 2, 3]
e.extend([3]) # now e = [1, 2, 3, 3]
f = [1, 2, 3]
f.extend(3) # TypeError

Why does assigning the list slice to another variable change the result?

Why would the following two examples have different results? I thought slicing a list would result in a (shallow) copy of the list elements, so a should not be changed in both cases.
>>> a = [1, 2, 3, 4, 5]
>>> a[3: 5] = [0, 0] # example 1
>>> a
[1, 2, 3, 0, 0] # elements in the original list are changed
>>> b = a[3: 5] # example 2
>>> b = [100, 100]
>>> a # elements in the original list are unchanged
[1, 2, 3, 0, 0]
List slices are different depending on the context:
a[3: 5] = [0, 0]
This is a slice assignment, which means to assign the values [0, 0] to a section in a. This clearly modifies a.
b = a[3: 5]
This creates a copy of a section of a and assigns it to b. It is unrelated to a. Modifying b won't affect a at all.
You confuse slicing with slice assignment. Slicing creates a new list, slice assignment modifies an existing list.
Modifies the list itself:
a = [1, 2, 3, 4, 5]
a[3: 5] = [0, 0] # example 1
Creates a new list for the sliced part:
b = a[3: 5] # example 2
Changes the content of b to some other new list
b = [100, 100]
Use id to check if they are identical:
a = [1, 2, 3, 4, 5]
a[3: 5] = [0, 0]
b = a[3: 5]
b = [100, 100]
print(id(a),id(b)) # (140150319362488, 140150319364288)
Slicing behaves differently depending on whether it is used in an expression (e.g. on the right hand side of an assignment) or in a target / left hand side of an assignment. If slicing created a shallow copy also on the left hand side, it would be impossible to change a slice of a list directly, and the result of the assignment would simply be lost.

How to use list slicing to append to end of list in python?

Following can be used to add a slice to append to front of list.
>>> a = [5,6]
>>> a[0:0] = [1,2,3]
>>> a
[1,2,3,5,6]
what slice to use to append to the end of list.
If you really want to use slice, you can use the length of a:
a = [5, 6]
a[len(a):] = [1, 2, 3]
a
output:
[5, 6, 1, 2, 3]
But the simplest is to directly extend a:
a = [5, 6]
a += [1, 2, 3] # or a.extend([1, 2, 3])
I think you should consider extend():
>>> a = [1, 2, 3]
>>> a.extend([4, 5, 6])
>>> a
[1, 2, 3, 4, 5, 6]
Both + and += operators are defined for list, which are semantically
similar to extend.
list + list2 creates a third list in memory, so you can return the
result of it, but it requires that the second iterable be a list.
list += list2 modifies the list in-place (it is the in-place operator,
and lists are mutable objects, as we've seen) so it does not create a
new list. It also works like extend, in that the second iterable can
be any kind of iterable.
Time Complexity
Append has constant time complexity, O(1).
Extend has time complexity, O(k).
Iterating through the multiple calls to append adds to the complexity,
making it equivalent to that of extend, and since extend's iteration
is implemented in C, it will always be faster if you intend to append
successive items from an iterable onto a list.
↳ More Information
>>> a = [1, 2, 3]
>>> a[len(a):] = [4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
or
>>> a = [1, 2, 3]
>>> a += [4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
You have got short answer from Jeevaa and Reblochon Masque but if you want to use for loop then try this:
a = [5,6]
b = [1,2,3]
for val in b[::-1]:#Reverse b and insert it to a
a.insert(0,val)
print(a)
Output
[1,2,3,5,6]

Mutability of lists in python

Example one: Changing the value that has been appended to b changes the value in the original list l
>>> l = [1 , 2, 3]
>>> b = []
>>> b.append(l)
>>> b[0].append(4)
>>> b
[[1, 2, 3, 4]]
>>> l
[1, 2, 3, 4]
Example 2: l1 is appended to ans and then the value of l1 is modified. However, the value in ans remains the same.
>>> l1 = [1, 2, 3]
>>> ans = []
>>> ans.append(l1)
>>> ans
[[1, 2, 3]]
>>> l1 = [2, 3, 4]
>>> ans
[[1, 2, 3]]
I seem to be missing some fundamental point about the mutability of lists. Could someone please point it out?
You are not mutating l1. You are assigning a new object to the same name, which makes that name point to a new object. You are moving a label, not modifying an object. The only remaining reference to the list that l1 used to point to is now ans[0].
In your first example, l is a pointer, as well as b.
l is then appended to b, so b[0] now refers to the pointer.
Next, you append 4 to b[0], which is the same thing as l, so 4 is added to both b[0] and l.
In your second example, ans contains the pointer of l1, just like b contained the pointer of l
Then, you changed l1 itself by assigning it to a different array, so l1 changed but ans[0] did not.
The biggest takeaway from this is that append just changes the list, and the pointer remains the same. But when you set a variable to a different list, the pointer changes.
Replace
>>> l1 = [2, 3, 4]
with
>>> l1[:] = [2, 3, 4]
That will not assign a new list to l1.

Can you copy a list in an element of another list?

I would like to know if there is a possibility of copying an entire list on an element of another one.
a = [1, 2, 3]
b = []
b[0] = list(a)
print(b)
It gives the error: list assignment index out of range
But if we do not copy it and we only put it there:
a = [1, 2, 3]
b = [a]
print(b)
It works and prints: [[1, 2, 3]]
Can this be done?
It gives the error: "list assignment index out of range"
Because you have created b as an empty list with b=[] and then you try and access the first element [0] of list b with b[0] when executing b[0] = list(a), and that index doesn't exist for an empty list.
To add a value to a list, you generally always use the list.append() method:
a = [1, 2, 3]
b = []
b.append(a) # similar to b = [a] but more intuitive.
print(b) # prints b = [[1, 2, 3]]
Simple, clear and concise.
A Note of caution:
b.append(a) and b.append(list(a)) are two similarly looking different things.
b.append(a) (similar to b = [a]) copies a reference of a in b while b.append(list(a)) creates a new list from the values of a and adds it to the list b. Because lists are mutable, this has comfusing implications when manipulating the internal list.
This becomes clearer with an example, where a = [1, 2, 3] and b=[] in each example:
With b.append(a):
b.append(a)
print(b) # prints b = [[1, 2, 3]]
# Now if we modify a:
a.append(4) # a = [1, 2, 3, 4]
# Changes are visible in b:
print(b) # b = [[1, 2, 3, 4]]
With b.append(list(a)):
b.append(list(a))
print(b) # prints b = [[1, 2, 3]]
# Now if we modify a:
a.append(4) # a = [1, 2, 3, 4]
# Changes are NOT visible in b:
print(b) # b = [[1, 2, 3]]
Python lists do not allow you to modify elements with an index equal to or greater than the length of the list. The first element of an empty list does not exist. You need to add it with b.append(list(a))

Categories

Resources