NamedTuples, Hashable and Python - python

Consider the following code:
#!/usr/bin/env python3.7
from typing import NamedTuple, Set
class Person(NamedTuple):
name: str
fred: Set[str]
p = Person("Phil", set())
print(p)
my_dict = {}
my_dict[p] = 10
print(my_dict)
which produces this error
Traceback (most recent call last):
File "./temp.py", line 14, in <module>
my_dict[p] = 10
TypeError: unhashable type: 'set'
In this case, it's sample code, and I have simplified it lots, so its
quite easy to see where the error comes from. typed.NamedTuple
obviousl calculates its hash based on all of its instances variables
and one of them is a set. When I discovered this, however, it was
painful to track down.
So, my question is, why is the error message showing this? Should it
not be TypeError: unhashable type: 'Person'. And why is the
traceback not coming from the bowels of python somewhere where the
error actually is.

NamedTuple is based on the tuple class. See collections.namedtuple()
The hash of a tuple is the combined hash of all the elements. See tupleobject.c
Since set is unhashable it is not possible to hash a tuple or NamedTuple containing a set.
And since the hashing of a set is implemented in C you don't see the traceback

Related

TypeError: 'list' object is not callable(python) [duplicate]

This question already has answers here:
Why does "example = list(...)" result in "TypeError: 'list' object is not callable"? [duplicate]
(14 answers)
Closed 3 years ago.
I have a simple script:
list = [1, 2, 3, 4, 5]
myrange = list(range(1, 10))
for number in list:
if number in myrange:
print(number, 'is between 1 and 10')
However, whenever I attempt to run my script, Python raises an error:
Traceback (most recent call last):
File "python", line 2, in <module>
TypeError: 'list' object is not callable
What does this error mean? Why am I getting it? And how can I fix it?
Before you can fully understand what the error means and how to solve, it is important to understand what a built-in name is in Python.
What is a built-in name?
In Python, a built-in name is a name that the Python interpreter already has assigned a predefined value. The value can be either a function or class object. These names are always made available by default, no matter the scope. Some of the values assigned to these names represent fundamental types of the Python language, while others are simple useful.
As of the latest version of Python - 3.6.2 - there are currently 61 built-in names. A full list of the names and how they should be used, can be found in the documentation section Built-in Functions.
An important point to note however, is that Python will not stop you from re-assigning builtin names. Built-in names are not reserved, and Python allows them to be used as variable names as well.
Here is an example using the dict built-in:
>>> dict = {}
>>> dict
{}
>>>
As you can see, Python allowed us to assign the dict name, to reference a dictionary object.
What does "TypeError: 'list' object is not callable" mean?
To put it simply, the reason the error is occurring is because you re-assigned the builtin name list in the script:
list = [1, 2, 3, 4, 5]
When you did this, you overwrote the predefined value of the built-in name. This means you can no longer use the predefined value of list, which is a class object representing Python list.
Thus, when you tried to use the list class to create a new list from a range object:
myrange = list(range(1, 10))
Python raised an error. The reason the error says "'list' object is not callable", is because as said above, the name list was referring to a list object. So the above would be the equivalent of doing:
[1, 2, 3, 4, 5](range(1, 10))
Which of course makes no sense. You cannot call a list object.
How can I fix the error?
If you are getting a similar error such as this one saying an "object is not callable", chances are you used a builtin name as a variable in your code. In this case the fix is as simple as renaming the offending variable. For example, to fix the above code, we could rename our list variable to ints:
ints = [1, 2, 3, 4, 5] # Rename "list" to "ints"
myrange = list(range(1, 10))
for number in ints: # Renamed "list" to "ints"
if number in myrange:
print(number, 'is between 1 and 10')
PEP8 - the official Python style guide - includes many recommendations on naming variables.
This is a very common error new and old Python users make. This is why it's important to always avoid using built-in names as variables such as str, dict, list, range, etc.
Many linters and IDEs will warn you when you attempt to use a built-in name as a variable. If your frequently make this mistake, it may be worth your time to invest in one of these programs.
I didn't rename a built-in name, but I'm still getting "TypeError: 'list' object is not callable". What gives?
Another common cause for the above error is attempting to index a list using parenthesis (()) rather than square brackets ([]). For example:
>>> lst = [1, 2]
>>> lst(0)
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
lst(0)
TypeError: 'list' object is not callable
For an explanation of the full problem and what can be done to fix it, see TypeError: 'list' object is not callable while trying to access a list.
Here is the mcve!
>>> []()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable
Try also {}() and ()(). The message TypeError: 'X' object is not callable means that you wrote expression(some_arguments) where expression is an instance of the X type, and this type doesn't support to be used in a function call syntax. Most of the time you wrote this because you thought expression was a function or some other callable type.

Python Iterating Through Dictionary With Tuples

I am designing a system that involves iterating through a pre-made non python standard dictionary with an iter function. I am trying to iterate through the tuples but keep getting this stupid Error. Also, this error only happens half the time, due to a bit flipping function that messes with inputs
TypeError: 'tuple' object cannot be interpreted as an integer
Probably an easy fix, can anyone spot?
Heres relevant code:
In my main program:
for tup in crusherdict.CrusherDict(db2, fields[0]):
log.write("VOTE\t{}\t{}\n".format(tup[0][0], tup[0][1]))
Place in Dictionary giving me the iter error:
def __iter__(self):
for i in range(self.__len__()):
yield self.db.fetch(entryName(self.name,i))
Heres db.fetch:
def fetch(self,key):
return self.cache[key]
And entryName:
def entryName(dict, str):
return (dict, "E", str)
Full BackTrace:
in <module>
if commands[line[0]](db, tempDict, logFile, line):
in cast
return inq(db, tempDict, logFile, ("INQ", tempDict["voterID"]))
line 100, in inq
for tup in crusherdict.CrusherDict(db3, fields[0]):
crusherdict.py", line 91, in __iter__
for i in range(self.__len__()):
TypeError: 'tuple' object cannot be interpreted as an integer
>>>
The traceback says the error happens at:
for i in range(self.__len__()):
We know that range expects an integer so this is hopeful, and indeed we can replicate the error message in Python 3:
>>> range((1,2))
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'tuple' object cannot be interpreted as an integer
Therefore __len__ is not returning an integer as it should.
Your function entryName() returns a tuple of three elements. This tuple is used by fetch as an index into your cache. Since we have to guess from the bits you provide, I'm guessing your cache is a plain list, and needs an integer index, not a tuple. (Also you're passing an int to an argument you named str, so you're definitely confused about something here.)

Does a variable override method here

I am starting to play around with python a little, and as a novice i tried this:
>>> s="";str=""
>>> for x in [ 1,2,3,4,5,6 ] :
... s += str(x)
...
Traceback (most recent call last):
File "<console>", line 3, in <module>
TypeError: 'str' object is not callable
I accidentally declared a variable called str (str is supposed to be a function).
Assuming it would break the semantics too much, even in a dynamically
typed language, is there a namespace i can use to qualify methods
like str and ensure this does not happen or make it difficult?
This is what import <module> instead of from <module> import * is used for. As long as you use str in the only meaning of local variable value in <module>, you can use
module.str elswhere, without mangling namespace.
The only tokens that can't be clashed are keywords. This is intended functionality and there is no way to prevent this: everything is an object in Python
You might want to use some IDE tools, p.ex. Eclipse+PyDev, that checks your code and warn for possible errors.
As per your question you have already defined str=""
so when you will call str method which converts values into string it will not call actual method in place of that it will call str="".
that's why you are getting error because you can not call a str object to convert int to string.

Python Error Handling

I was trying to convert the list to a set, with the following code:
set1=set(list1)
the code was running fine, but all on a sudden started to give the following error,
set1=set(list1)
TypeError: unhashable type: 'list'
please let me know how may I resolve.
And sometimes some good running program gives error all on a sudden with no parameter changed, how may I debug it?
Your error suggests that your list contains a list. Lists are mutable and thus can't be hashed for use in a set or a dictionary. One work-around is to convert your list into a tuple using tuple(some_list), but if they're heavily nested, it becomes more complex.
Your list contains another list:
>>> set([['contained list'], 1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Sets can only contain items that are hashable; any standard type that cannot be mutated is hashable, but a list is not (it can be mutated). By contrast, a tuple is not mutable and can be stored in a set.
Bugs happen, even in code that has been running fine for a while. Debug it with print statements, or better still, by using a debugger like the pdb.
If your bug only appears intermittently, use a try/except block to catch the error, then print out information or use a debugger to figure out what is going on:
try:
set1=set(list1)
except TypeError:
print 'list1 not hashable? contents: %r' % list1
# optionally: import pdb; pdb.set_trace()
raise

TypeError: list objects are unhashable

totalCost = problem.getCostOfActions(self.actions)
Looks like you are trying to use a list as key in a dictionary or something like that. Lists are not hashable, and so they cannot be used as dictionary keys or in sets.
On another note, python gives you a stacktrace when such an error happens, and that includes file-names and line-numbers. You should be able to track down the offending code with that.
Edit About stacktraces:
cat > script.py
foo = [1,2,3]
bar = {}
bar[foo] = "Boom"
print "Never happens"
python script.py
Traceback (most recent call last):
File "script.py", line 3, in <module> // this is the file and the line-number
bar[foo] = "Boom"
TypeError: unhashable type: 'list'
You've probably attempted to use mutable objects such as lists, as the key for a dictionary, or as a member of a set. Mutable items cannot be tracked for such uses efficiently and predictably so they do not provide the hash special attribute.
The error is produced when an unhashable type is added to sets.
>>> s=set((1,2))
>>> a.add([3,4])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
I think this may be also your case. Use tuple instead of list:
>> a.add((3,4))
>>>
Maybe the line should be like this instead
totalCost = sum(map(problem.getCostOfActions,self.actions))
or if you prefer a generator expression
totalCost = sum(problem.getCostOfActions(action) for action in self.actions)
Since I can't see your code, I assumed that problem.getCostOfActions() returns the cost of a single action, as that might cause the error you are getting if self.actions is a list
Alternatively you could fix the function problem.getCostOfActions() so that it returns the total cost of a list of actions as the name suggests.
Consider adding the body of this function to your question if you want help fixing it
I had the same error with django :
food_list = ['spam' , 'more spam' , 'spam spam']
table.cum.add(food_list)
and I got error - TypeError: list objects are unhashable.
The fix is table.cum.add(*food_list)
-add * infront of list to unpack it
the model method add accepts args - (x,y,z) but not ([x,y,z])
hope it helps

Categories