Welcome to Cadabra Q&A, where you can ask questions and receive answers from other members of the community.
0 votes

Hi Troops,

I'm trying to use Python functions to modify Cadabra expressions but I'm getting some results that seem odd to me.

The following code contains three simple Python functions. They each add one term to an expression. I start with a single term and expect, after each call, to see one term added to the expression. Starting with Q^{a} I expect to get Q^{a}+R^{a} then Q^{a}+R^{a}+S^{a} and finally Q^{a}+R^{a}+S^{a}+T^{a}. But the first function foo does not return an updated expression. I've included print statements in the functions to monitor the expression. They show that the expression has been changed within the function but in the case of foo that change hasn't been passed back to the main code.

Am I doing something wrong here? Should I always use a pure Python functions (like bah)? And the final question -- is the function kludge legal?

Cheers, Leo

{a,b,c,d,e,f}::Indices.

def foo (obj):

    ans := @(obj) + R_{d}.
    print ("foo: "+str(ans))
    obj = ans
    print ("foo: "+str(obj))

def bah (obj):

    ans := @(obj) + S_{d}.
    print ("bah: "+str(ans))
    return ans

def kludge (obj):

    ans := @(obj) + T_{d}.
    print ("kludge: "+str(ans))
    substitute (obj,$@(obj)->@(ans)$)
    print ("kludge: "+str(obj))

Gamma := Q_{d}.

foo (Gamma)
print (Gamma)

Gamma = bah (Gamma)
print (Gamma)

kludge (Gamma)
print (Gamma)
in General questions by (1.6k points)

1 Answer

0 votes
 
Best answer

That's standard Python behaviour. The obj parameter of foo is a 'handle' to the expression object which you pass in. Any changes that you make to the expression 'pointed to' by obj will be visible outside foo. But assigning obj to something else just changes the handle. After the obj=ans line, obj no longer points to the expression you wanted to modify, but instead it points to the expression which was created on the first line of foo.

This pure-Python example does the same thing:

def foo(obj):
    bar = [5,6,7]
    obj = bar
    print(obj)

x = [1,2,3]
foo(x)
print(x)

See e.g. https://stackoverflow.com/questions/22054698/python-modifying-list-inside-a-function for more on this.

So the question now is how to change the object 'pointed to' by obj. Just like with the list example, you have two options. Either do as in your bah. Or directly manipulate 'elements of the object pointed to by obj'. That is effectively what you do in kludge, but yes, it is kludgy.

There are some methods to manipulate the object pointed to by obj directly, but they are not ready for prime time yet (I'll update this answer later when I have more time). So for the time being, do as in bah.

by (76.4k points)
selected by

Hi Kasper, Thanks for quick reply and great explanation. I think its time I put a bit more effort into learning Python properly :). I'll stick with the 'bah' approach. Cheers, Leo

...