I'm using python3 and networkx to construct a graph. Networkx is really great, but let's say I want to put some nice objects in as nodes. Here is my object maker (a simple class, for the purposes of demonstration):
class DictToObject:
def __init__(self, dic):
for key, value in dic.items():
setattr(self, key, value)
def __eq__(self, other):
return self.id == other.id
And then here are some nodes:
import networkx
s1 = DictToObject({"name":"sarah", "id":"s"})
s2 = DictToObject({"name":"sarah", "id":"s"})
M = networkx.DiGraph()
M.add_edge(s1, s2)
Due to overriding __eq__
, I get the error:
Traceback (most recent call last):
File "networkxcapabilitytest.py", line 16, in <module>
M.add_edge(s1, s2)
File "/usr/local/lib/python3.4/site-packages/networkx/classes/digraph.py", line 485, in add_edge
if u not in self.succ:
TypeError: unhashable type: 'DictToObject'
This is very upsetting. Overriding __eq__
seemed like the "right" way to let networkx identify when two nodes should be considered the "same". Perhaps I should submit this issue to the networkx developers. Or perhaps there is a networkx built-in or better way to do this.
Following @jonrsharpe's comment:
class DictToObject:
def __init__(self, dic):
for key, value in dic.items():
setattr(self, key, value)
def __eq__(self, other):
return self.id == other.id
def __hash__(self):
return hash(self.__dict__.values())
def __repr__(self):
return str(self.__dict__.values())
import networkx
s1 = DictToObject({"name":"sarah", "id":"s"})
s2 = DictToObject({"name":"sarah", "id":"s"})
M = networkx.DiGraph()
M.add_edge(s1, s2)
print( M.nodes() )
print( M.edges() )
Output:
[dict_values(['s', 'sarah'])]
[(dict_values(['s', 'sarah']), dict_values(['s', 'sarah']))]
Note that, there is only one node in the graph now. Since the hash()
produces (id's equal) same thing, hence it does not add the second node to the graph.
As discussed in the comment, a better solution can be provided at the following link.
Indeed, the solution proposed before had some problems with the hash
function and it gives problem in case you want to do print(M)
or even nx.draw(M)
.
Then, the improved solution is:
import networkx as nx
class DictToObject:
def __init__(self, data):
self._data = data
def __eq__(self, other):
return self._data['id'] == other._data['id']
def __hash__(self):
return hash(self._data['id'])
s1 = DictToObject({"name":"sarah", "id":"s"})
s2 = DictToObject({"name":"sarah", "id":"s"})
G = nx.Graph()
G.add_edge(s1, s2)
print(G)
nx.draw(G)