1

I'm writing a python module in C and I need to have it call a Python function with one of the arguments passed by "reference". The end result should be that what the Python function does to the argument gets saved into the original C variable.

int      *resume;      // this int is actually passed in to this body of code
PyObject *resumeInt;   // which was originally a C callback func for libnids, but
PyObject *ret;         // I removed/rewrote most of this code for clarity

resumeInt = Py_BuildValue("i",-1);
ret = PyObject_CallFunction(tcpResumeFunc, "(O)", resumeInt);    

*resume = PyInt_AsLong(resumeInt);
Py_DECREF(ret);
Py_DECREF(resumeInt);

To test, I had the Python function that tcpResumeFunc represents modify the passed-in integer to = 5. When I print out *resume at the end of this code, however, it retains it's initial -1 value. I know I am misunderstanding something about how the API works. Any suggestions?

1 Answer 1

4

I think you're misunderstanding something about how variables work in Python. Variables in Python do not contain values; they refer to them - like in Java, except there are no "primitives" (not even int). Assignment to a variable doesn't overwrite the old value; it simply causes the variable to refer to the new value and cease referring to the old one (which may then be garbage-collected if nothing else refers to it).

Why not just actually return (and use) a value (since Python functions always return something anyway, even if it's just the implicit return of None)?


Edit: Working through an example, because it's too long for a comment reply.

From C, we call PyObject_CallFunction, which hands a PyObject* over to the Python function. In Python, the function parameter (we'll call it spam) now refers to the pointed-at PyObject.

When we write spam += 6 in the function, that is the same as spam = spam + 6. The bytecode interpreter gets the PyObject that represents the value 6, runs a special bytecode that inspects the value represented by the two objects, adds them, and creates a new PyObject (or fetches one from a cache) that represents the sum.

Then spam is rebound to the new object; but the spam variable-reference is on the Python side of the memory fence, and is not the same thing as the PyObject* on the C side of the fence.

Thus, resumeInt does not get pointed at the newly created/fetched PyObject.

It is actually behaving the exact same way as it would calling the Python function from Python. Try it:

def fry(spam):
  spam += 1

eggs = 3
fry(eggs)
eggs # still 3!

This happens because rebinding spam does not affect eggs, which is a separate variable. We are not passing by reference; we are passing a reference by value. Just like in Java.

Sign up to request clarification or add additional context in comments.

2 Comments

I do understand that, (I think) I assumed that since *resumeInt referred to the same object, after passing it to PyObject_CallFunction I would be left with the new *resumeInt as modified by the Python function called. Then I just convert it back to a C variable with PyInt_AsLong. Is there something wrong with that idea? Your suggestion does sound a bit more elegant and easier, but I'd like to understand this.
Ah, after your edit it makes more sense. So what I'm trying to do would probably be impossible. I will modify the code to have it return the value. I'd vote up but I don't have enough reputation; great answer though, thanks a lot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.