Search code examples
pythonclasssubclassinit

Subclass super().__init__(*args, **kwargs) not working. Says object.__init__() takes exactly one argument (the instance to initialize) when it doesn't


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!


Solution

  • 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.