on the 'immutability' of tuples in python [duplicate] - python

This question already has answers here:
python tuple is immutable - so why can I add elements to it
(4 answers)
Closed 1 year ago.
If tuples are supposed to be immutable (which are in most cases), why can we change their data in the following way :
tuple = (1, 2, 3)
tuple += (4, 5)
print (tuple) #outputs (1, 2, 3, 4, 5)

You didn't mutate a tuple, you made a new one.
>>> tup = (1, 2, 3)
>>> tup2 = tup
>>> tup
(1, 2, 3)
>>> tup2
(1, 2, 3)
>>> tup += (4, 5)
>>> tup
(1, 2, 3, 4, 5)
>>> tup2
(1, 2, 3)
Initially, tup and tup2 refer to the same tuple. After the tup += (4, 5), tup refers to a new tuple and tup2 still refers to the old one. No mutation occurred.

Tuples are immutable which means you cannot update or change the values of tuple elements. You are able to take portions of existing tuples to create new tuples.

In your example, += sneakily stops being an in-place operator. tuple is a different tuple from when you started, because adding two tuples necessarily produces a new one, because tuples are immutable.

Related

Understanding dictionary assignment with tuple and list

I am a python beginner and I recently learned about dictionary assignment.
Here's what I am attempting
my_list = [[1, 4], [2, 2],[5,1]]
lists = dict(my_list)
print(lists) # Prints {1:4,2:2,5:1}
my_list = [(1, 4), (2, 2),(5,1)]
lists = dict(my_list)
print(lists) # Prints {1:4,2:2,5:1}
my_list = [[1, 4], (2, 2),{5,1}]
lists = dict(my_list)
print(lists) # Prints {1:5,2:2}
I am unable to explain why we are getting this weird answer in example 3.
Kindly help and explain.
{5, 1} is a set and, as such, inherently unordered. It is, somewhat depending on your Python implementation, unpredictably iterated as either 1->5 or 5->1. If you catch the first case, your dict instantiation is equivalent to:
lists = dict([(1, 4), (2, 2), (1, 5)])
or, even more verbose and obvious
lists = {}
lists[1] = 4
lists[2] = 2
lists[1] = 5 # overrides first binding of 1
Since there can be no duplicate keys in a dict, the last key binding for a repeated key "wins".

Behaviour of set that stores tuples, Python

I'd like to create a set of tuples, but have been encountering some behaviour that I don't understand. For example:
>>> b=set()
>>> b.add((1,2))
>>> b.add((4,5))
>>> b
{(1, 2), (4, 5)}
# all fine
>>> f = set((1,2,3))
>>> f
{1, 2, 3}
# ?
>>> b=set().add((1,2))
>>> b
# b is empty?
Can someone help me understand this behaviour? My understanding is that tuples are hashable, so I should be able to store them in a set. I'm using Python 3.
You are confusing different things.
set() takes an iterable and creates a set from the contents.
If you want to create a set with a single tuple, pass in an iterable with that single tuple:
>>> set([(1, 2, 3)])
{(1, 2, 3)}
It's better to use the {...} set literal syntax:
>>> {(1, 2, 3)}
{(1, 2, 3)}
set().add() updates a set in-place and returns None.
If you wanted to create an empty set and add to that, do so in two separate expressions:
>>> b = set()
>>> b.add((1, 2))
>>> b
{(1, 2)}
Neither of these two issues have anything to do with tuples being hashable.
set((1, 2, 3)) means a set of (1, 2, 3)'s elements. A set whose only element is (1, 2, 3) is {(1, 2, 3)}, not set((1, 2, 3)).
set.add modifies a set in place. If you want to add to a set, you need to store the set itself, then add to it, not store add's return value.

Can a set have lists as its elements? [duplicate]

This question already has answers here:
Add list to set
(13 answers)
How to make a set of lists
(4 answers)
How to convert list of lists to a set in python so I can compare to other sets?
(3 answers)
Closed 5 years ago.
I am trying to get unique lists which can consist duplicate values,can I use sets to get unique lists?
To be more specific ,here is an example:
my_list=[[1,2,1],[1,2,2],[1,2,2],[1,1,2],[2,2,2]]
what i would want is :
set_list=[[1,2,1],[1,2,2],[1,1,2],[2,2,2]]
is this possible?
Thanks in advance for your kind response :)
No, a list is not hashable. You will get the following error:
TypeError: unhashable type: 'list'
Given the list only contains hashable objects, you can however convert the list to a tuple and add the tuples. So you could do something like:
>>> my_list=[[1,2,1],[1,2,2],[1,2,2],[1,1,2],[2,2,2]]
>>> set_tuples = {tuple(a_list) for a_list in my_list}
>>> set_tuples
{(1, 2, 2), (1, 2, 1), (2, 2, 2), (1, 1, 2)}
You can then for instance construct a uniqueness filter with:
my_list=[[1,2,1],[1,2,2],[1,2,2],[1,1,2],[2,2,2]]
result = []
unique_set = set()
for sublist in my_list:
the_tuple = tuple(sublist)
if the_tuple not in unique_set:
unique_set.add(the_tuple)
result.append(sublist)
So all operations on the set are done with tuples. This gives:
>>> result
[[1, 2, 1], [1, 2, 2], [1, 1, 2], [2, 2, 2]]
Lists are not hashable, but you can have a set of tuples, :
set(map(tuple, my_list))
# {(1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 2, 2)}

Does enumerate over slice perform sublist materialization?

Does this:
for i,v in enumerate(lst[from:to]):
or this:
for i,v in enumerate(itertools.islice(lst,from,to)):
...make a copy of iterated sublist?
Assuming that lst is a regular Python list, and not a Numpy array, Pandas dataframe, or some custom class supporting slice indexing, then the slice [...:...] will create a new list, whereas itertools.islice does not.
As suggested in comments, you can see this for yourself by creating both enumerate objects and modifying the original list before consuming them:
>>> lst = [1, 2, 3, 4, 5]
>>> e1 = enumerate(lst[1:4])
>>> e2 = enumerate(itertools.islice(lst, 1, 4))
>>> del lst[2] # remove second element
>>> list(e1)
[(0, 2), (1, 3), (2, 4)] # shows content of original list
>>> list(e2)
[(0, 2), (1, 4), (2, 5)] # second element skipped
Also note that this does in fact have nothing to do with enumerate, which will create a generator in both cases (on top of whatever iterable was created before by the slice).
You could also just create the two variants of slices and check their types:
>>> type(lst[1:4])
list # a new list
>>> type(itertools.islice(lst, 1, 4))
itertools.islice # some sort of generator

List comprehension with tuple assignment

I want to ask if something like this is possible in python:
a,b = [i,i+1 for i in range(5)]
I know this isn't possible because I have got an error, but I think you understand what I am trying to achieve. Let me clear it up, I can do :
a,b = 3+2,3
Edit ---> Or even better:
a,b = [0,1,2,3,4],[1,2,3,4,5]
I wan't a similar thing in my first code example. I am trying to assign variables 'a' and 'b' as list, with list comprehension, but using tuple as assignment, the point is I don't want to use this:
a = [i for in range(5)]
b = [i+1 for in range(5)]
I am aware that I can use this: t = [(i,i+1) for i in range(5)], but that's not the point.
By the way this is only a simple example => "i,i+1"
Edit ---> I would like to clarify my question. How to assign several variables (type list) in one line, using list comprehension?
When you run this:
a,b = [(i,i+1) for i in range(5)] # wrapped i, i+1 in parentheses (syntax error)
It makes a list of five two-item tuples, like this:
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
But you're trying to assign those five tuples to only two objects (a and b)
Using argument unpacking (*) in zip, you can "unzip" the output to the first and second elements of each tuple:
a,b = zip(*[(i,i+1) for i in range(5)])
Which is this:
[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5)]
And can be assigned to a and b as you've written
Don't try to be clever. This is perfectly acceptable code:
>>> a = range(5)
>>> b = range(1,6)
>>> a, b
([0, 1, 2, 3, 4], [1, 2, 3, 4, 5])

Categories