PyList_GetItem() returning a reference - python

I am using the PyHelper class that I found HERE
My python script is returning a list and in C++ I can call PyList_Size() and successfully get the size but when I try get item it returns a very large number which I assume is a address reference rather than the actual list element
Here is my code
args is defined as CPyObject args = PyTuple_New(1);
This is the Output
I am expecting to see the actual elements which are in the list that I can then assign to a string in order to use them in C++ without having to use PyObject further

Related

Auto expand table range in excel

Tying to find to an Excel VBA equivalent to
sheet.range('A1').expand('table')
#https://docs.xlwings.org/en/stable/datastructures.html
I've tried to create a xlwings func like this :
#xw.func
def expand(rng, caller):
sht = caller.sheet
return sht.range(rng).expand().address
=expand("C7") returns "$C$7:$E$8" (works)
So I've tried for feed this rng as string inside the following macro (that spots changes within a range)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng_s As String
rng_s = expand("C7") #This is where there is the error
Set rng = Target.Worksheet.Range(rng_s)
If Not Intersect(Target, rng) Is Nothing Then my_macro2 (rng)
End Sub
#The Python console returns : TypeError: The Python instance can not be converted to a COM object
Any idea how to make this table expand automatically? Or make this xlwings function work?
You need to call the function directly instead of using xlwings way as they need to correct the code in order to support calling from another function/sub.
Use this in your function instead of calling expand
rng_s = Py.CallUDF("udftest", "expand", Array("C7", Nothing), ThisWorkbook, ThisWorkbook.ActiveSheet.Range("C7"))
Change ThisWorkbook.ActiveSheet for the Sheet where the range you are looking for belongs, that last parameter is passed as the caller in the defined python function, so even you could use only that parameter do get the desired range in your expand function.

Python pass list as argument sends all of list data or just reference

I have the following code:
def test(a):
a.append(3)
test_list = [1,2]
test(test_list)
In the above code, when I pass test_list as an argument to test function, does python send the whole list object/data (which is a big byte size) or just the reference to the list (which would be much smaller since its just a pointer)?
From what I understood by looking at Python's pass by object reference, it only sends the reference, but do not know how to verify that it indeed is the case
It's passing an alias to the function; for the life of the function, or until the function intentionally rebinds a to point to something else (with a = something), the function's a is an alias to the same list bound to the caller's test_list.
The straightforward ways to confirm this are:
Print test_list after the call; if the list were actually copied, the append in the function would only affect the copy, the caller wouldn't see it. test_list will in fact have had a new element appended, so it was the same list in the function.
Print id(test_list) outside the function, and id(a) inside the function; ids are required to be unique at any given point in time, so the only way two different lists could have the same ID is if one was destroyed before the other was created; since test_list continues to exist before and after the function call, if a has the same id, it's definitionally the same list.
All function arguments are passed by reference in Python. So the a variable in the function test will refer to the same object as test_list does in the calling code. After the function returns, test_list will contain [1,2,3].

If strings in Python are immutable, why can I change a string in a function?

I'm new to Python, and I'm writing a function to change the case of all characters in a string. The function itself is called swapCases(), and I'm using the library function swapcase() within my own function. Here is the code that I use to test my function:
print(swapCases("HoLa"))
In the first case, my function reads:
def swapCases(s):
for i in range(len(s)):
s[i] = s[i].swapcase()
return s
When I run this code, I get a message from the compiler: "str object does not support item assignment." I Googled the message and it said that strings in Python are immutable and therefore can't be changed. However, when I change my function to this:
def swapCases(s):
s = s.swapcase()
return s
the function does exactly what I want, and the test prints out "hOlA." If strings are immutable in Python, why does this work? Doesn't being immutable mean that I can't change them?
By assigning it to the variable s, you are reassigning s. This gets rid of the reference to the old string "HoLa" and replaces it with a reference to the string returned from s.swapcases()
In your original case, you are attempting to modify the string index by index. Doing this would be mutating the existing references, which is not allowed. This is what is meant by immutable.
Your function is not modifying a string object. It's modifying a name assigned to it. When you assign directly, like this,
s = "abc"
s[2] = "z"
...you are saying "change the third character in this particular string object to 'z'." On the other hand, if you assign twice, like this,
s = "abc"
s = "abz"
...you are saying "change the name, s, to refer to a new string object." This applies whether it's created as a local variable (as above) or as a function argument.

Pass one element of tuple as argument along with other arguments

I have a library function that returns a tuple and looks something like this
def some_function(some_string):
does something
return (text,id)
Now I want to pass the text returned from the some_function as argument to another function. The catch is the function has other arguments as well and I don't want to pass the entire tuple as a pointer. I also need need to retrieve many texts that will be generated by different values of some_string.
Depending on the condition met, I want to call another function which will look something like this
if abcd:
other_function(name,phone,**some_function("abcd")**,age)
elif xyz:
other_function(name,phone,**some_function("xyz")**,age)
else:
other_function(name,phone,**some_function("aaaa")**,age)
So what should I replace some_function("abcd") with so that it sends only the text and not both text and id as arguments?
The other_function is defined like this
def other_function(name,phone,text,age):
...
return
One solution that I came up with myself was to create another function that returns just the text.
def returntextonly(some_string):
self.some_string = some_string
(text,id) = some_function(some_string)
return text
and then call the other_function like
if abcd:
other_function(name,phone,returntextonly("abcd"),age)
I mostly program in C++ and have picked up python only recently. I was wondering if there is a better solution to the problem than creating a new function just to return one element of the tuple.
Thanks for reading.
You can run this as:
other_function(name, phone, some_function("abcd")[0], age)
There is no need to define the additional if statements, wrapper functions etc, since you want to pass only the first element of the tuple which is returned from the original function.
For the general case, this becomes:
other_function(name, phone, some_function(some_string)[0], age)
Note that the tuple is another (immutable) iterator, and its elements can be accessed using the regular indexes like in lists.

How do I pull a list from an MArgList in maya?(Python)

Edit(solution?): I was able to achieve this by converting my arrays into a string and parsing through and recreating them on the other end. If anyone has a better way I would appreciate it.
Original:
I created a class that extends MPxCommand to create a plug-in and have been trying to figure out how to parse the MArgList in the doIt() function to pull a couple of lists from from the args variable. I am trying to pull a list of strings and a list ints that will have an inconsistent length so I won't be able to (I think) fill the flag multiple times when I call the command.
I've had success pulling individual variables with MArgParser but have not found a way to pull a full list.
MArgList appears to have what I want with the function asStringArray(index) and asIntArray(index) but when I use try using them I get the error:
# Error: TypeError: file Command.py line 163: in method 'MArgList_asIntArray', argument 2 of type 'unsigned int &' #
Edit:
class myCommand(omMPx.MPxCommand):
def __init__(self):
omMPx.MPxCommand.__init__(self)
def newSyntax(self):
syntax = om.MSyntax()
syntax.addFlag("-s", "-string", om.MSyntax.kString)
return syntax
def doIt(self, args):
stringFlag = "-s"
parser = om.MArgParser(self.newSyntax(),args)
if parser.isFlagSet(stringFlag):
self.myStr = parser.flagArgumentString(stringFlag, 0)
print self.myStr
This code will put "hello" from the args into self.myStr if I run cmds.myCommand(s = "hello"), but I'd like to be able to be able to run cmds.myCommand(s = ["hello", "world"]) and be able to get the array from the args variable and put it into self.myStr.
I hope that clears up what I'm trying to do.
I think your problem is in the declaration of that function:
'MArgList_asIntArray', argument 2 of type 'unsigned int &'
int & is a reference to an int, i.e. an int you give to the function, the function will change it, and your variable will reflect the changes.
So maybe you already see it: passing a constant is not possible.
I'm not into python at all, so I don't know if it would be sufficient to simply declare an int, initialize it and then pass it as argument 2 to the function.

Categories