I am working on making a subclass of the rustworkx.PyGraph class, and I am getting some strange behavior. Namely, it does not allow me to pass keyword arguments to my class.
Here is a minimal example:
import rustworkx
class Graph(rustworkx.PyGraph):
def __init__(self, multigraph=False):
super().__init__(multigraph=multigraph)
regular_graph = rustworkx.PyGraph(multigraph=False)
print(f"The first graph is a multigraph: {regular_graph.multigraph}")
try:
my_graph = Graph()
except Exception as e:
print(f"Error: {e}")
This is outputing
The first graph is a multigraph: False
Error: object.__init__() takes exactly one argument (the instance to initialize)
I have taken a look at the source code for the class definition in Python, and everything seems to be fine for this to work:
class PyGraph(Generic[_S, _T]):
attrs: Any
multigraph: bool = ...
def __init__(self, /, multigraph: bool = ...) -> None: ...
def add_edge(self, node_a: int, node_b: int, edge: _T, /) -> int: ...
and I thought that I could work around this by just setting the variable explicilty before any of the edges had a chance to be added, but that also has not worked:
import rustworkx
class Graph(rustworkx.PyGraph):
def __init__(self, multigraph=False):
super().__init__()
self.multigraph = multigraph
regular_graph = rustworkx.PyGraph(multigraph=False)
print(f"The first graph is a multigraph: {regular_graph.multigraph}")
try:
my_graph = Graph()
except Exception as e:
print(f"Error: {e}")
The first graph is a multigraph: False
Error: attribute 'multigraph' of 'rustworkx.PyGraph' objects is not writable
I am not sure what to do at this point, so any help explaining what is going on and how to fix it would be greatly appreciated. Thank you!
You're not looking at the actual source code. The actual source code is written in Rust. You're looking at type stubs, which don't match the actual implementation.
rustworkx.PyGraph
doesn't actually define __init__
. PyO3, the Rust/Python compatibility layer they're using, doesn't support that. What it does support is __new__
:
Only Python's
__new__
method can be specified,__init__
is not available.
So if you define __new__
instead of __init__
, at least this particular error should be resolved:
class Graph(rustworkx.PyGraph):
def __new__(cls, multigraph=False):
return super().__new__(cls, multigraph=multigraph)
However, it doesn't look like subclassing is a use case the rustworkx devs put much work in to support. The docs don't seem to mention __new__
anywhere, and the type stubs list __init__
instead of __new__
, a discrepancy that causes problems for subclasses, but very few problems for other use cases.
You're likely to run into more weird behavior when trying to write this subclass. Pickling failing, overloads getting ignored, etc. You may want to consider using composition instead of inheritance.