How to compare unbound instancemethod equality in python/C

142 Views Asked by At

I am trying to port some python 2.7 classes I have into a C extension module -- well D actually, but that should not matter here.

One of my classes provides richcompare, for which two objects should compare equal if (appart from some C attributes) one of their types' methods is identical. For example, the desired behaviour would be (let mytype be the type that provides richcompare and 'method' the name of the instancemethod that needs comparison):

class X(mytype) :
    def method(self) : pass

class Y(X) :
    pass

class Z(X) :
    def method(self) : pass

x=X(); y=Y(); z=Z()

x==y # True
x==z # False

I tried to implement this test in C by comparing the PyObject* pointers returned from

auto self_type = cast(PyObject*) py_self.ob_type;
auto other_type = cast(PyObject*) py_other.ob_type;
PyObject* self_method = PyObject_GetAttrString(self_type, "method");
PyObject* other_method = PyObject_GetAttrString(other_type, "method");

if (self_method != other_method) equal = false;

Py_XDECREF(self_method);
Py_XDECREF(other_method);

and was surprised that the two pointers are not equal, even when the TypeObjects are.

I then checked in plain python and indeed:

Python 2.7.12+ (default, Sep 17 2016, 12:08:02) 
[GCC 6.2.0 20160914] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class X(object) :
...     def method(self) : pass
... 
>>> x=X()
>>> type(x).method == X.method
True
>>> type(x).method is X.method
False
>>> 

The returned methods are not identical. So, my two questions:

1) Why are the unbound methods X.method not identical?

2) How can I test their equality using the C API?

1

There are 1 best solutions below

0
harfel On BEST ANSWER

I worked it out now.

1) method lookup seems to create a new object that wraps the python function into a new object. This is why even

X.method is X.method

returns False.

2) PyMethod_Function(method) can be used to obtain the function (PyObject *) from the method object. This indeed implements the desired behaviour:

auto self_type = cast(PyObject*) py_self.ob_type;
auto other_type = cast(PyObject*) py_other.ob_type;
PyObject* self_method = PyObject_GetAttrString(self_type, "propensity");
PyObject* other_method = PyObject_GetAttrString(other_type, "propensity");
if (PyMethod_Function(self_method != PyMethod_Function(other_method))
    equal = false;
Py_XDECREF(self_method);
Py_XDECREF(other_method);

Robust code should check whether the respective attributes are indeed PyMethods before obtaining their functions.