Cases where use of tuple is a must - python

In Python, the only difference between a list and a tuple that I know of is "lists are mutable but tuples are not". But as far as I believe, it depends on whether the coder wants to risk mutability or not.
So I was wondering whether there are any cases where the use of a tuple over a list is a must. Things that can not be done with a list but can be done with a tuple?

You can use tuples as keys in dictionaries and insert tuples into sets:
>>> {}[tuple()] = 1
>>> {}[list()] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Which is basically a result of a tuple being hashable while a list isn't:
>>> hash(list())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash(tuple())
3527539

The answer by #Otto is very good. The only thing I have to add to it is that when you open things up to 3rd party extensions, you really need to consult the documentation. Some functions/methods may expect one or the other data-type (or have different results depending on which one you use). One example is using tuples/lists to index a numpy array:
import numpy as np
a=np.arange(50)
a[[1,4,8]] #array([1, 4, 8])
a[(1,4,8)] #IndexError
EDIT
Also, a quick timing test shows that tuple creation is MUCH FASTER than list creation:
import timeit
t=timeit.timeit('[1,2,3]',number=10000000)
print (t)
t=timeit.timeit('(1,2,3)',number=10000000)
print (t)
which is good to keep in mind. In other words, do:
for num in (8, 15, 200):
pass
instead of:
for num in [8, 15, 200]:
pass

Also, the now obsolete string formatting using the % operator requires the argument list to be a tuple. A list would be treated as single argument:
>>> "%s + %s" % [1, 2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
>>> "%s + %s" % (1, 2)
'1 + 2'

Related

TypeError: 'Series' objects are mutable, thus they cannot be hashed to modify and excel file converted into a data frame

import pandas as pd
import itertools
data = pd.read_excel("Kitchen.xlsx")
dict_data={}
for i in range(data.shape[0]):
if(data["Yes_No"][i]=="yes"):
if(data["Menu"] in dict_data.keys()):
dict_data[data["Menu"][i]].append(data["Specification"][i])
else:
dict_data[data["Menu"][i]]=list(data["Specification"][i])
menu=pd.read_csv("Menu.csv")
menu["Menu"]=""
for i in range(menu.shape[0]):
name=menu["Name"][i]
comb=list(FC["Combinations"][i].split("+"))
l=[]
for c in comb:
l.append(dict_data[c])
all_combinations=list(itertools.product(*l))
for men_cmb in all_combinations:
s=""
for item in men_cmb:
s+=str(item)+"||"
menu["Menu"][i]=s
i+=1
menu.to_csv("Menu.csv")
I want to print a combination of ingredients for a menu. This is my first time usong itertools so i dont really know what that error means. I basicly want to print all posible combinatios to do a menu with different ingredients. Menu.csv is my final excel but in Kitchen.xls i have the ingredients.
this is the output
C:\VEpython\Scripts\Drivers\Scripts>C:/Users/212803155/AppData/Local/Programs/Python/Python38-32/python.exe "c:/VEpython/Scripts/Drivers/Scripts/import pandas as pd.py"
Traceback (most recent call last):
File "c:/VEpython/Scripts/Drivers/Scripts/import pandas as pd.py", line 7, in <module>
if(data["Menu"] in dict_data.keys()):
File "C:\Users\212803155\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pandas\core\generic.py", line 1798, in __hash__
raise TypeError(
TypeError: 'Series' objects are mutable, thus they cannot be hashed
C:\VEpython\Scripts\Drivers\Scripts>
Your error is more fundamental and not related to itertools: mutable types are not hashable unless they provide a hash method!
This happens with simple types too, but the error is clearer.
>>> set().add({'a':1})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

Different ValueError for the same operation in List and Tuple

I am curious why ValueErrors are different in List and Tuple when I try to get an index. ValueError of a list returns in well format with actual argument "ValueError: 'ITEM' is not in list", whereas tuple returns something like this "ValueError: tuple.index(x): x not in tuple".
I think List and Tuple both are calling same index() method then why it is raising different ValueErrors?
>>> jframe_li
['Angular', 'React', 'Vue.js', 'Ember.js', 'Mereor', 'Node.js', 'Backbone.js']
>>> jframe_tu
('Angular', 'React', 'Vue.js', 'Ember.js', 'Mereor', 'Node.js', 'Backbone.js')
>>> jframe_li.index('React')
1
>>> jframe_tu.index('React')
1
>>> jframe_li.index('react')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 'react' is not in list
>>> jframe_tu.index('react')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: tuple.index(x): x not in tuple
There are implementation differences in the index methods for lists and tuples, including in the text of a raised ValueError.
See ValueError string for tuple.index and ValueError string for list.index

Extending peewee expressions throws 'Expression' object is not iterable

I want to dynamically create where-clauses in peewee. I learned I need to use expressions, but cannot get this to run. Here my code:
clauses = [
(Docs.language_frst == 0),
(Docs.body_len <= max_body_len),
(Docs.body_len >= min_body_len)
]
if len(aValid_ids_used):
clauses.extend( (Docs.id.not_in(aValid_ids_used)) )
docids = Docs.select(Docs.id).where(reduce(operator.and_, clauses))
As long as aValid_ids_used is empty the code runs fine. Once aValid_ids_used is not empty any longer and I am asking the clauses to be extended I get an error:
Traceback (most recent call last): File "xyz.py", line 170, in <module>
clauses.extend( (Docs.id.not_in(aValid_ids_used)) )
TypeError: 'Expression' object is not iterable
You need to pass a list to extend with. You're currently passing an Expression
clauses.extend( (Docs.id.not_in(aValid_ids_used), ) )
e.g.
>>> c = []
>>> c.extend((2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> c.extend((2,))
>>> c
[2]
You probably want to use list.append rather than list.extend. extend takes a list as an argument, while append takes a single item.

Why can't I add <some list>.append to a python set?

Why is it that I can add normal callables and methods to a set, but not <some list>.append (for instance)?
For Example:
>>> l = []
>>> s = set()
>>> s.add(l.append)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> type(l.append)
<type 'builtin_function_or_method'>
>>> type(map)
<type 'builtin_function_or_method'>
>>> s.add(map)
>>> def func(): print 'func'
...
>>> s.add(func)
>>> print s
set([<built-in function map>, <function func at 0x10a659758>])
Edit: I noticed that l.append.__hash__() also gives this error
You cannot add lists to a set because lists are mutable. Only immutable objects can be added to sets.
l.append is an instance method. You can think of it as if it were the tuple (l, list.append) — that is, it's the list.append() method tied to the particular list l. The list.append() method is immutable but l is not. So while you can add list.append to the set you can't add l.append.
This works:
>>> s.add(list.append)
This does not work:
>>> s.add((l, list.append))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
You're not trying to add list.append. You're trying to add l.append which is an instance method, not a class method. list instances aren't hashable, and apparently their methods aren't hashable either.
Think of it this way. You have 2 lists:
lfoo = []
lbar = []
Now you want to add their respective appends to your set:
s = set()
s.add(lfoo.append)
s.add(lbar.append)
Now when you do the hash lookup for the set, you can't just rely on the function portion of the instance method. Indeed, lfoo and lbar ultimately use the same function (list.append). So that isn't a unique hash. The way you make it unique is by attaching it to an instance. However, the instance doesn't support hashing, so the set has no way of telling the difference between lfoo.append and lbar.append.
In Python, list objects aren't hashable. I suspect that when you hash an instance method, it incorporates the hash of the instance its bound to. As such, you get the TypeError.

clarification on comparing objects of different types

The following sentences are a cause of confusion for me(from Guido's Tutorial on python.org):
"Note that comparing objects of
different types is legal. The outcome
is deterministic but arbitrary: the
types are ordered by their name. Thus,
a list is always smaller than a
string, a string is always smaller
than a tuple, etc."than a tuple, etc."
That means that for :
a=[90]
b=(1)
a<b
the result should be True. But it is not so!
Can you help me out here?than a tuple, etc."
Also, what is meant by "The outcome is deterministic but arbitrary"?
(1) is an int. You probably meant (1,), which is a tuple.
Please note that you should not rely upon this behavior anymore. Some built-in types cannot be compared with other built-ins, and new data model provides a way to overload comparator functionality.
>>> set([1]) > [1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only compare to a set
Moreover, it was removed in py3k altogether:
>>> [1,2] > (3,4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: list() > tuple()
>>> [1,2] > "1,2"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: list() > str()

Categories

Resources