Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #99068 > unrolled thread

Why doesn't this method have access to its "self" argument?

Started byRobert Latest <boblatest@yahoo.com>
First post2015-11-19 16:13 +0000
Last post2015-11-20 08:18 +0100
Articles 4 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  Why doesn't this method have access to its "self" argument? Robert Latest <boblatest@yahoo.com> - 2015-11-19 16:13 +0000
    Re: Why doesn't this method have access to its "self" argument? Chris Angelico <rosuav@gmail.com> - 2015-11-20 04:34 +1100
    Re: Why doesn't this method have access to its "self" argument? Peter Otten <__peter__@web.de> - 2015-11-19 19:00 +0100
    Re: Why doesn't this method have access to its "self" argument? dieter <dieter@handshake.de> - 2015-11-20 08:18 +0100

#99068 — Why doesn't this method have access to its "self" argument?

FromRobert Latest <boblatest@yahoo.com>
Date2015-11-19 16:13 +0000
SubjectWhy doesn't this method have access to its "self" argument?
Message-ID<db6apoFgvjlU1@mid.individual.net>
Hi all,

I'm still trying to find a simple way to implement a method (but not the
full class) in C. Suggestions I have recived so far are good, but seem to be
over the top for such a (seemingly) simple problem. I came up with the idea
of implementing the class in Python, and just setting the method after the
actual class definition. See example below. The "py_method" is added to the
class later on, and it seems to become a proper method of the class (and its
instances) just as if it had been defined inside the class itself. Of course
it also has access to its "self" argument.

If I do the same thing with a method implemented in the external module
"cmethod", and I call that method on an instance, it gets passed a NULL
pointer as its "self" argument and so has no access to the instance's "data"
attribute.

I found a workaround using a wrapper method which calls a C function,
passing the instance as a separate argument. It works, and I cannot see any
disadvantage. It's just not as elegant as I'd like it to be, and I don't
understand WHY the C "method" doesn't receive a pointer to the Python
instance. Maybe somebody can clarify.


Here's what happens when I build, install and run the minimal example below:

rl@dc:~/c/cwsf/python_module$ python test.py
py_method(): <__main__.Test instance at 0xb71f5a2c>
c_method(): self at (nil)
c_function(): self at (nil), instance at 0xb71f5a2c
<__main__.Test instance at 0xb71f5a2c>
'Some data'
c_function(): self at (nil), instance at 0xb71f5a2c
<__main__.Test instance at 0xb71f5a2c>
'New Data'
rl@dc:~/c/cwsf/python_module$

Minimal example files:


========================== test.py ===================================

import cmethod

class Test():
    def __init__(self):
        self.data = "Some data"

    def wrapper(self):
        return cmethod.c_function(self)

def py_method(self):
    print "py_method(): %s" % repr(self)

# add methods to the class "after the fact"
Test.py_method = py_method
Test.c_method = cmethod.c_method

foo = Test()
foo.py_method() # works as expected
foo.c_method()  # is passed NULL in its self argument, why?
foo.wrapper()   # works fine, updates "data" attribute
foo.wrapper()   # works fine, sees updated "data" attribute

==================== cmethod.c =======================================

#include <Python.h>
#include <stdio.h>

static PyObject *c_method(PyObject *self, PyObject *args) {
    (void) args; /* not used */
    fprintf(stderr, "c_method(): self at %p\n",
        (void*) self); /* always prints 0x0 */
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *c_function(PyObject *self, PyObject *args) {
    PyObject *instance;
    PyObject *o;
    /* Retrieve instance from wrapper through argument tuple: WORKS */
    PyArg_UnpackTuple(args, "c_function", 1, 1, &instance);
    fprintf(stderr, "c_function(): self at %p, instance at %p\n",
        (void*) self, (void*) instance);
    PyObject_Print(instance, stderr, 0); fputc('\n', stderr);
    /* Get and set attributes of instance: WORKS */
    o = PyObject_GetAttrString(instance, "data");
    PyObject_Print(o, stderr, 0); fputc('\n', stderr);
    PyObject_SetAttrString(instance, "data",
        PyString_FromString("New Data"));
    /* Side question: Do I have to DECREF "o"? */
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef methods[] =
{
     {"c_method", c_method, METH_VARARGS, "I want to be a class method"},
     {"c_function", c_function, METH_VARARGS, "I am a function"},
     {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initcmethod(void)
{
     (void) Py_InitModule("cmethod", methods);
}


======================== setup_cmethod.py ===========================

from distutils.core import setup, Extension

module1 = Extension('cmethod', sources = ['cmethod.c'])

setup (name = 'cmethod',
        version = '0.1',
        description = 'struggling to implement a class method in C',
        ext_modules = [module1])


robert

[toc] | [next] | [standalone]


#99075

FromChris Angelico <rosuav@gmail.com>
Date2015-11-20 04:34 +1100
Message-ID<mailman.472.1447954477.16136.python-list@python.org>
In reply to#99068
On Fri, Nov 20, 2015 at 3:13 AM, Robert Latest via Python-list
<python-list@python.org> wrote:
> rl@dc:~/c/cwsf/python_module$ python test.py
>
> Minimal example files:
>
> ========================== test.py ===================================
>
> import cmethod
>
> class Test():
>
> # add methods to the class "after the fact"
> Test.py_method = py_method
> Test.c_method = cmethod.c_method

Are you using Python 2 here? If so, you're using an old-style class,
which has specific consequences that I can never remember. Does the
situation change if you slap 'object' between those parentheses? Or if
you use Python 3?

ChrisA

[toc] | [prev] | [next] | [standalone]


#99077

FromPeter Otten <__peter__@web.de>
Date2015-11-19 19:00 +0100
Message-ID<mailman.474.1447956061.16136.python-list@python.org>
In reply to#99068
Robert Latest via Python-list wrote:

> I found a workaround using a wrapper method which calls a C function,
> passing the instance as a separate argument. It works, and I cannot see
> any disadvantage. It's just not as elegant as I'd like it to be, and I
> don't understand WHY the C "method" doesn't receive a pointer to the
> Python instance. Maybe somebody can clarify.

I don't know much about the C-implementation side, but functions written in 
Python are also descriptors. Given

def f(self): pass

class A(object):
    m = f
    c = abs
    v = 42

a = A()

a seemingly simple attribute access

m = a.m      # m is now a bound method

invokes f.__get__(a, A) under the hood while

m = a.c
assert m is abs

just returns the function (abs in the example) the same way it returns any 
other value:

v = a.v
assert v == 42

[toc] | [prev] | [next] | [standalone]


#99128

Fromdieter <dieter@handshake.de>
Date2015-11-20 08:18 +0100
Message-ID<mailman.507.1448003893.16136.python-list@python.org>
In reply to#99068
Robert Latest via Python-list <python-list@python.org> writes:

> I'm still trying to find a simple way to implement a method (but not the
> full class) in C. Suggestions I have recived so far are good, but seem to be
> over the top for such a (seemingly) simple problem.

The C interface of Python is far from simple - and it is very easy
to make severe and difficult to analyse errors. Therefore, I confirm
an advice you already got: use "cython" (which drastically reduces
the danger of errors).

A "method" in Python is just a function which happens to get automatically
on call an additional first arguemnt: the object's reference. Apparently,
this automatism does not work for C implemented functions (at least
in Python 2). You would need to use a (so called) "descriptor" wrapper
to do this explicitely.

The easiest way (in my view), however would be:

import _my_c_implementation

class C(object):
  def my_method(self, *args, **kw):
    return _my_c_implementation.my_method(self, *args, **kw)


With a descriptor "method" (sketched above), this would become:

class C(object):
  my_method = method(_my_c_implementation.my_method)

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web