Mailing List Archive

Using my routines as functions AND methods
Hi,

In my cpython i have written quite some functions to modify "objects".
and their python syntax is e.g.\

translate(obj, vec). e.g whereas obj is ALWAYS first argument.

on c side this functions looks like:
PyObject *python_translate(PyObject *self, PyObject *args, PyObject *kwargs)

this works great when specifying a list of functions in PyModuleDef
structure.

However, I also want to use these functions as class methods without having
to
write the function , twice. When using the SAME function as a methos, the
args tuple must insert/contain "self" in the first location, so i have
written a function to do that:

PyObject *python_oo_args(PyObject *self, PyObject *args) // returns new
reference,
{
int i;
PyObject *item;
int n = PyTuple_Size(args);
PyObject *new_args = PyTuple_New(n + 1);
PyTuple_SetItem(new_args, 0, self);

for (i = 0; i < PyTuple_Size(args); i++) {
item = PyTuple_GetItem(args, i);
PyTuple_SetItem(new_args, i + 1, item);
}
return new_args;
}

To fill in method array, i have created a #define like this:

#define OO_METHOD_ENTRY(name,desc) \
{ #name, (PyCFunction) ( [ ] (PyObject *self, PyObject *args) -> PyObject
* { \
PyObject *new_args = python_oo_args(self, args); \
PyObject *result = python_##name(self, new_args, NULL); \
return result; } ), METH_VARARGS | METH_KEYWORDS, (desc)},

(this uses a lambda function)

and use this in the array as:

PyMethodDef PyOpenSCADMethods[] = {
OO_METHOD_ENTRY(translate,"Move Object")
OO_METHOD_ENTRY(right,"Right Object")

Using this i can reuse all the functions as methods,
but its not 100% stable/bulletproof and crashes sometimes.
So there is the bug ?
Is there a better approach to reach my goal ?


thank you
--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 03/01/2024 22:47, Guenther Sohler via Python-list wrote:
> Hi,
>
> In my cpython i have written quite some functions to modify "objects".
> and their python syntax is e.g.\
>
> translate(obj, vec). e.g whereas obj is ALWAYS first argument.

> However, I also want to use these functions as class methods without having
> to
> write the function , twice. When using the SAME function as a methos, the
> args tuple must insert/contain "self" in the first location, so i have
> written a function to do that:

I'm probably missing something obvious here but can't you
just assign your function to a class member?

def myFunction(obj, ...): ...

class MyClass:
myMethod = myFunction


Then you can call it as

myObject = MyClass()
myObject.myMethod()

A naive example seems to work but I haven't tried anything
complex so there is probably a catch. But sometimes the simple
things just work?

--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 1/3/2024 8:00 PM, Alan Gauld via Python-list wrote:
> On 03/01/2024 22:47, Guenther Sohler via Python-list wrote:
>> Hi,
>>
>> In my cpython i have written quite some functions to modify "objects".
>> and their python syntax is e.g.\
>>
>> translate(obj, vec). e.g whereas obj is ALWAYS first argument.
>
>> However, I also want to use these functions as class methods without having
>> to
>> write the function , twice. When using the SAME function as a methos, the
>> args tuple must insert/contain "self" in the first location, so i have
>> written a function to do that:
>
> I'm probably missing something obvious here but can't you
> just assign your function to a class member?
>
> def myFunction(obj, ...): ...
>
> class MyClass:
> myMethod = myFunction
>
>
> Then you can call it as
>
> myObject = MyClass()
> myObject.myMethod()
>
> A naive example seems to work but I haven't tried anything
> complex so there is probably a catch. But sometimes the simple
> things just work?

That works if you assign the function to a class instance, but not if
you assign it to a class.

def f1(x):
print(x)
f1('The plain function')

class Class1:
pass

class Class2:
pass

c1 = Class1()
c1.newfunc = f1
c1.newfunc('f1 assigned to instance') # Works as intended

Class2.newfunc = f1
c2 = Class2()
c2.newfunc('f1 assigned to class') # Complains about extra argument


--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 1/3/2024 11:17 PM, Thomas Passin wrote:
> On 1/3/2024 8:00 PM, Alan Gauld via Python-list wrote:
>> On 03/01/2024 22:47, Guenther Sohler via Python-list wrote:
>>> Hi,
>>>
>>> In my cpython i have written quite some functions to modify "objects".
>>> and their python syntax is e.g.\
>>>
>>> translate(obj, vec). e.g whereas obj is ALWAYS first argument.
>>
>>> However, I also want to use these functions as class methods without
>>> having
>>> to
>>> write the function , twice. When using the SAME function as a methos,
>>> the
>>> args tuple must insert/contain "self" in the first location, so i have
>>> written a function to do that:
>>
>> I'm probably missing something obvious here but can't you
>> just assign your function to a class member?
>>
>> def myFunction(obj, ...): ...
>>
>> class MyClass:
>>      myMethod = myFunction
>>
>>
>> Then you can call it as
>>
>> myObject = MyClass()
>> myObject.myMethod()
>>
>> A naive example seems to work but I haven't tried anything
>> complex so there is probably a catch. But sometimes the simple
>> things just work?
>
> That works if you assign the function to a class instance, but not if
> you assign it to a class.
>
> def f1(x):
>     print(x)
> f1('The plain function')
>
> class Class1:
>     pass
>
> class Class2:
>     pass
>
> c1 = Class1()
> c1.newfunc = f1
> c1.newfunc('f1 assigned to instance') # Works as intended
>
> Class2.newfunc = f1
> c2 = Class2()
> c2.newfunc('f1 assigned to class')  # Complains about extra argument

If your requirements are not very tricky, you can write a
convert-to-method function yourself:

def f1(x):
print(x)
f1('The plain function')

class Class2:
pass

def convert_method(f):
"""Assign existing method without a "self" arg
as a class's method.
"""
def fnew(instance, *args):
f(*args)
return fnew

Class2.newfunc = convert_method(f1)
c2 = Class2()
c2.newfunc('f1 assigned as method of Class2') # Prints the arg

This example does not make f1 aware of the self argument, but you asked
to convert an existing function, and that function would not be aware of
the self parameter. It's much like a decorator function, but is not here
being used as a decorator. If you meant something else, please think out
what you want and explain that.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 1/3/2024 11:17 PM, Thomas Passin wrote:
> On 1/3/2024 8:00 PM, Alan Gauld via Python-list wrote:
>> On 03/01/2024 22:47, Guenther Sohler via Python-list wrote:
>>> Hi,
>>>
>>> In my cpython i have written quite some functions to modify "objects".
>>> and their python syntax is e.g.\
>>>
>>> translate(obj, vec). e.g whereas obj is ALWAYS first argument.
>>
>>> However, I also want to use these functions as class methods without
>>> having
>>> to
>>> write the function , twice. When using the SAME function as a methos,
>>> the
>>> args tuple must insert/contain "self" in the first location, so i have
>>> written a function to do that:
>>
>> I'm probably missing something obvious here but can't you
>> just assign your function to a class member?
>>
>> def myFunction(obj, ...): ...
>>
>> class MyClass:
>>      myMethod = myFunction
>>
>>
>> Then you can call it as
>>
>> myObject = MyClass()
>> myObject.myMethod()
>>
>> A naive example seems to work but I haven't tried anything
>> complex so there is probably a catch. But sometimes the simple
>> things just work?
>
> That works if you assign the function to a class instance, but not if
> you assign it to a class.
>
> def f1(x):
>     print(x)
> f1('The plain function')
>
> class Class1:
>     pass
>
> class Class2:
>     pass
>
> c1 = Class1()
> c1.newfunc = f1
> c1.newfunc('f1 assigned to instance') # Works as intended
>
> Class2.newfunc = f1
> c2 = Class2()
> c2.newfunc('f1 assigned to class')  # Complains about extra argument

If your requirements are not very tricky, you can write a
convert-to-method function yourself:

def f1(x):
print(x)
f1('The plain function')

class Class2:
pass

def convert_method(f):
"""Assign existing method without a "self" arg
as a class's method.
"""
def fnew(instance, *args):
f(*args)
return fnew

Class2.newfunc = convert_method(f1)
c2 = Class2()
c2.newfunc('f1 assigned as method of Class2') # Prints the arg

This example does not make f1 aware of the self argument, but you asked
to convert an existing function, and that function would not be aware of
the self parameter. It's much like a decorator function, but is not here
being used as a decorator. If you meant something else, please think out
what you want and explain that.

--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
Thank you for your answers.
apparently I did not express myself clear enough. Let me rephrase.

I got an embedded C function like this:

==
PyObject *python_translate(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *kwlist[] = {"obj", "v", NULL};
PyObject *v = NULL;
PyObject *obj = NULL;
double x = 0, y = 0, z = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist,
&obj,
&v
)) {
PyErr_SetString(PyExc_TypeError, "Error during parsing
translate(object,vec3)");
return NULL;
}
...

PyMethodDef PyOpenSCADFunctions[] = {
{"translate", (PyCFunction) python_translate, METH_VARARGS |
METH_KEYWORDS, "Move Object."},
...

static PyModuleDef OpenSCADModule = {
PyModuleDef_HEAD_INIT,
"openscad",
"OpenSCAD Python Module",
-1,
PyOpenSCADFunctions,
...
};

============
From within python i can successfully use it with

translate(my_object, [2,0,0])

my_object is an instance of embedded class PyOpenSCADType, so alternatively
I want to use my function as method too, like so:

my_object.translate([2,0,0])

WITHOUT duplicating the code for python_translate

So the idea is to call the translate-function from within the translate
method and insert "self" into the args tuple.
However my solution somewhat works but is not very stable and quite prone
to crash.

Which are my alertnatives ?


On Wed, Jan 3, 2024 at 11:47?PM Guenther Sohler <guenther.sohler@gmail.com>
wrote:

> Hi,
>
> In my cpython i have written quite some functions to modify "objects".
> and their python syntax is e.g.\
>
> translate(obj, vec). e.g whereas obj is ALWAYS first argument.
>
> on c side this functions looks like:
> PyObject *python_translate(PyObject *self, PyObject *args, PyObject
> *kwargs)
>
> this works great when specifying a list of functions in PyModuleDef
> structure.
>
> However, I also want to use these functions as class methods without
> having to
> write the function , twice. When using the SAME function as a methos, the
> args tuple must insert/contain "self" in the first location, so i have
> written a function to do that:
>
> PyObject *python_oo_args(PyObject *self, PyObject *args) // returns new
> reference,
> {
> int i;
> PyObject *item;
> int n = PyTuple_Size(args);
> PyObject *new_args = PyTuple_New(n + 1);
> PyTuple_SetItem(new_args, 0, self);
>
> for (i = 0; i < PyTuple_Size(args); i++) {
> item = PyTuple_GetItem(args, i);
> PyTuple_SetItem(new_args, i + 1, item);
> }
> return new_args;
> }
>
> To fill in method array, i have created a #define like this:
>
> #define OO_METHOD_ENTRY(name,desc) \
> { #name, (PyCFunction) ( [ ] (PyObject *self, PyObject *args) ->
> PyObject * { \
> PyObject *new_args = python_oo_args(self, args); \
> PyObject *result = python_##name(self, new_args, NULL); \
> return result; } ), METH_VARARGS | METH_KEYWORDS, (desc)},
>
> (this uses a lambda function)
>
> and use this in the array as:
>
> PyMethodDef PyOpenSCADMethods[] = {
> OO_METHOD_ENTRY(translate,"Move Object")
> OO_METHOD_ENTRY(right,"Right Object")
>
> Using this i can reuse all the functions as methods,
> but its not 100% stable/bulletproof and crashes sometimes.
> So there is the bug ?
> Is there a better approach to reach my goal ?
>
>
> thank you
>
>
--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 04/01/2024 04:17, Thomas Passin via Python-list wrote:

>> I'm probably missing something obvious here but can't you
>> just assign your function to a class member?
>>
>> def myFunction(obj, ...): ...
>>
>> class MyClass:
>> myMethod = myFunction
>
> That works if you assign the function to a class instance, but not if
> you assign it to a class.
>
> def f1(x):
> print(x)
> f1('The plain function')
>
> class Class1:
> pass
>
> class Class2:
> pass
>
> c1 = Class1()
> c1.newfunc = f1
> c1.newfunc('f1 assigned to instance') # Works as intended
>
> Class2.newfunc = f1
> c2 = Class2()
> c2.newfunc('f1 assigned to class') # Complains about extra argument

Yes but I did the assignment inside the class definition and
that seemed to work just fine:

>>> def g(obj, st): print(st, obj.v)
...
>>> class D:
... def __init__(self,v): self.v = v
... m = g
...
>>> d = D(66)
>>> g(d,'val = ')
val = 66
>>> d.m('v = ')
v = 66


--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
Hi list

The approach with defining the methods from python appears to be a very
good idea and it also works for classes, defined in python side.
However, when I try this one:

def mytrans(self):
print(self)

c=cube()
cls=c.__class__

cls.trans=mytrans

I get this:

Traceback (most recent call last):

File "<string>", line 8, in <module>

TypeError: cannot set 'trans' attribute of immutable type 'PyOpenSCAD'

The Problem is probably that PyOpenSCAD is a class not defined on python
side but on c++ side,
I fear that I need to solution in c++ side. ...



On Wed, Jan 3, 2024 at 11:47?PM Guenther Sohler <guenther.sohler@gmail.com>
wrote:

> Hi,
>
> In my cpython i have written quite some functions to modify "objects".
> and their python syntax is e.g.\
>
> translate(obj, vec). e.g whereas obj is ALWAYS first argument.
>
> on c side this functions looks like:
> PyObject *python_translate(PyObject *self, PyObject *args, PyObject
> *kwargs)
>
> this works great when specifying a list of functions in PyModuleDef
> structure.
>
> However, I also want to use these functions as class methods without
> having to
> write the function , twice. When using the SAME function as a methos, the
> args tuple must insert/contain "self" in the first location, so i have
> written a function to do that:
>
> PyObject *python_oo_args(PyObject *self, PyObject *args) // returns new
> reference,
> {
> int i;
> PyObject *item;
> int n = PyTuple_Size(args);
> PyObject *new_args = PyTuple_New(n + 1);
> PyTuple_SetItem(new_args, 0, self);
>
> for (i = 0; i < PyTuple_Size(args); i++) {
> item = PyTuple_GetItem(args, i);
> PyTuple_SetItem(new_args, i + 1, item);
> }
> return new_args;
> }
>
> To fill in method array, i have created a #define like this:
>
> #define OO_METHOD_ENTRY(name,desc) \
> { #name, (PyCFunction) ( [ ] (PyObject *self, PyObject *args) ->
> PyObject * { \
> PyObject *new_args = python_oo_args(self, args); \
> PyObject *result = python_##name(self, new_args, NULL); \
> return result; } ), METH_VARARGS | METH_KEYWORDS, (desc)},
>
> (this uses a lambda function)
>
> and use this in the array as:
>
> PyMethodDef PyOpenSCADMethods[] = {
> OO_METHOD_ENTRY(translate,"Move Object")
> OO_METHOD_ENTRY(right,"Right Object")
>
> Using this i can reuse all the functions as methods,
> but its not 100% stable/bulletproof and crashes sometimes.
> So there is the bug ?
> Is there a better approach to reach my goal ?
>
>
> thank you
>
>
--
https://mail.python.org/mailman/listinfo/python-list
Re: Using my routines as functions AND methods [ In reply to ]
On 2024-01-03 23:17:34 -0500, Thomas Passin via Python-list wrote:
> On 1/3/2024 8:00 PM, Alan Gauld via Python-list wrote:
> > On 03/01/2024 22:47, Guenther Sohler via Python-list wrote:
> > > Hi,
> > >
> > > In my cpython i have written quite some functions to modify "objects".
> > > and their python syntax is e.g.\
> > >
> > > translate(obj, vec). e.g whereas obj is ALWAYS first argument.
^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> >
> > > However, I also want to use these functions as class methods without having
> > > to
> > > write the function , twice. When using the SAME function as a methos, the
> > > args tuple must insert/contain "self" in the first location, so i have
> > > written a function to do that:
> >
> > I'm probably missing something obvious here but can't you
> > just assign your function to a class member?
> >
> > def myFunction(obj, ...): ...
^^^
> >
> > class MyClass:
> > myMethod = myFunction
> >
> >
> > Then you can call it as
> >
> > myObject = MyClass()
> > myObject.myMethod()
> >
> > A naive example seems to work but I haven't tried anything
> > complex so there is probably a catch. But sometimes the simple
> > things just work?
>
> That works if you assign the function to a class instance, but not if you
> assign it to a class.
>
> def f1(x):
> print(x)

You omitted the first argument (obj).

That should be

def f1(obj, x):
print(x)


> f1('The plain function')

>
> class Class1:
> pass

o = Class1()
f1(o, 'The plain function')

works for me.


> class Class2:
> pass
>
> c1 = Class1()
> c1.newfunc = f1
> c1.newfunc('f1 assigned to instance') # Works as intended

Now this doesn't work any more (but the OP doesn't want that anyway,
AFAICT).


> Class2.newfunc = f1
> c2 = Class2()
> c2.newfunc('f1 assigned to class') # Complains about extra argument

But this does.

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"