I have some Python classes being generated automatically using SWIG and I want to extend them.
The problem is, those classes have another method other than __init__
, called create
that returns a class instance (they are the real init).
I want to extend those classes with new methods but keep the original name.
I tried to implement __new__
method but it returns an instance of the parent class
I get why it happens, but I can't seem to find a way to change it while keeping the create
methods.
Let me show you an example with my current implementation attempt in order to explain:
File a:
class A:
def __init__(self, ...):
pass
def create(self, ...):
# do some stuff
return object_of_A
File b:
from a import A as AClass
class A(AClass):
def __init__(self, ...):
pass
def __new__(cls, ...):
# do some stuff
return super(A, cls).create(...)
def foo(self):
print('Hi')
Wanted behavior:
>>> from b import A
>>> a = A(...)
>>> a.foo()
>>> Hi
Actual behavior:
>>> from b import A
>>> a = A(...)
>>> a.foo()
>>> AttributeError: 'A' object has no attribute 'foo'
Thanks!
Here goes a simple example. I have created a simple static method for creating objects in C++ and wrapped this with SWIG. Afterwards, I extend the class with the same name for changing the method slightly.
example.h
class A {
public:
static int Create(A** obj);
A() = default;
~A() = default;
int test();
};
example.cpp
#include "example.h"
int A::test() {
return 5;
}
int A::Create(A** obj) {
*obj = new A();
return 0;
}
example.i
%module example
%{
#include "example.h"
%}
%typemap(in, numinputs=0) A **obj (A *temp) {
$1 = &temp;
}
%typemap(argout) A ** {
PyObject* temp = NULL;
if (!PyList_Check($result)) {
temp = $result;
$result = PyList_New(1);
PyList_SetItem($result, 0, temp);
}
// Create shadow object (do not use SWIG_POINTER_NEW)
temp = SWIG_NewPointerObj(SWIG_as_voidptr(*$1),
$descriptor(A*),
SWIG_POINTER_OWN | 0);
PyList_Append($result, temp);
Py_DECREF(temp);
}
%include "example.h"
setup.py
#!/usr/bin/env python
from distutils.core import setup, Extension
setup(name="example",
py_modules=['example'],
ext_modules=[Extension("_example",
["example.i", "example.cpp"],
swig_opts=['-c++'],
extra_compile_args=['--std=c++11']
)]
)
extension.py
from example import A as parentA
class A(parentA):
def __init__(self):
super(A,self).__init__()
def SmartMethod(self):
return 2
@staticmethod
def Create():
retval, obj = parentA.Create()
obj.__class__ = A
return obj
The new class defined in extension.py
inherits all functionalities of the original (if any) and here an example of slightly changing the Create method.
The trick with changing the __class__
attribute converts the new object into the descendant, so SmartMethod
can be called. If you add additional members to the descendant, you must add these in the overloaded Create
method.