2

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.

mareoraft
  • 3,474
  • 4
  • 26
  • 62

1 Answers1

3

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.


Edit

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)
AndreP
  • 105
  • 1
  • 5
Sait
  • 19,045
  • 18
  • 72
  • 99
  • So far so good! Thanks! – mareoraft Jan 11 '16 at 23:36
  • 1
    @mareoraft, I tried the solution proposed and there is, in my opinion, a **big problem** with it. I ended up having a Key Error when I try `networkx.draw(M)` or `print(M)`. Therefore, since I have the same problem to solve, I opened a _discussion on the repository of the library_. The repository's maintainers helped me out, now I have a working solution. I share with you the [link of the discussion](https://github.com/networkx/networkx/discussions/6742), since I cannot reply under this question anymore :( . – AndreP Jun 14 '23 at 14:24
  • 1
    @AndreP has a good point. The original answer is problematic because the hash function hashes all the dictionary values when it should just hash the id itself. I would recommend following his link and using the suggested approach there. Thanks Andre! – mareoraft Jun 15 '23 at 17:07