[Maya Python] Troubleshooting with Print (Len())

python
maya

#1

I just learned from a Stack Overflow post that

print (Len()) 

returns None. So you have to modify it to print a value such as follows

children = [1,2,3]
numChild = len(children)
print (numChild)

However, this does not work on my script. The script below creates a curve based on a joint chain. The script works. No errors. My question here is purely speculative.

I have 3 joints in a chain selected. It should return a value of 2. However, when I try to execute the print len command, it runs 0 or none.

Is there something I am missing out?

children=[]

def joint_positions(j):
    pos = [cmds.xform(j,q=True, t=True, ws=True)]
    children = cmds.listRelatives(j, c=True) or []
    for child in children:
        pos.extend(joint_positions(child))
    return pos

numChild = len(children)

print (numChild) #this line prints out zero. it should print out 2

joints = joint_positions('spine01_jnt') 
crv = cmds.curve(d = 1, p=joints)
cmds.rebuildCurve( crv, rt=0, s=len(children), d=3 )

The code above is modified from a post in Stack Overflow

Thank you for looking at the problem


#2

It seems to me there’s a few things going on:

  1. I don’t think that was the correct take-away from the first Stack Overflow post. print(len(children)) is just fine, and prints what you’d expect. The print statement itself doesn’t have a return value (returns None), so if you did something like word = print('Hello') then word would have no value, but the print function would still work properly. But in Python 2.7, which Maya uses, that doesn’t matter anyway, it’s a syntax error… that’s getting beside the point. I think the real take-away is about the difference between a statement returning something and printing something.

  2. To debug this, instead of printing the length, I would just print the whole children list. print(children). In my test, your listRelatives command works, but only on the immediate child. You’ll need the allDescendents flag set to True as well.

  3. But the real reason that your print statement prints 0 is the sequence that the commands are executed in. children is defined as empty, then your function is defined (but it hasn’t been called yet). Then you evaluate the length of children (still empty), since those statements aren’t inside the function. Then you call your function with ‘spine01_jnt’ afterwards.

Hope that helps!

Edit: Finally, I’m not sure a recursive function is necessary here, I think it’s a bit overly complicated. The allDescendents flag will return a list of everything, in which case you can just iterate on the list without calling the function recursively.


#3

Hi @stwert. Thanks for the response.

You are right about number (1). I guess I was just overthinking thinking it.

With regard to (3) " the sequence that the commands are executed in. children is defined as empty, then your function is defined (but it hasn’t been called yet)"

Oh. Gotcha. That makes senses. However, I tried moving the print command after the definition is called for (i.e. after the joints = , and crv = statement)

But I still get a zero or an empty list whenever I run the print function.

This is really weird to me since the spans flag takes in number values. And it does not error out.

So I 'm wondering why I’m still not getting a 2 whenever I run the print function.


#4

If you put the print command outside the function, even after it is called, I believe it gets into problems with the scope of the variable children. children is modified in the scope of the function, but after the function finishes, I could be wrong, but I don’t believe that the new values are accessible from outside. Someone else might be able to explain better. Put the print statement inside the function.

With respect to the spans argument, the command still works with span=0. 0 seems to be a valid number for spans (see the options box GUI).

Edit: To avoid this issue, why don’t you use the length of joints (divided by 3??) instead of children?


#5

This - @bentraje children is defined outside the scope of the function, so you’ll need to parse it into the function or by reference. Your returning a list of positions anyway so just get their length.

Can you not just call this:

print (len(joint_positions(j)))

If you need the children:

children=[]

def joint_positions(j, children):

    # because children is a mutable list you need to
    # append to it or extend it.

    pos = [cmds.xform(j,q=True, t=True, ws=True)]
    children.extend(cmds.listRelatives(j, c=True) or [])
    
    for child in children:
        pos.extend(joint_positions(child, children))

    return pos

positions = joint_positions(j, children)
numChild = len(children)

#6

Thank you for the responses!

@stwert

RE: spans = 0
Yea, I didn’t realize it until you pointed it out. Hahaha. I ended going for the chalk’s suggestion.

RE: To avoid this issue, why don’t you use the length of joints (divided by 3??) instead of children?
You are right! Same with chalk’s response. I initially thought by “length” you mean the actual length of joints in the scene, but its actually the python len command. So I was not really sure how to respond at that time.

Thanks!

RE: The allDescendents flag will return a list of everything,
You are right! Will revise it accordingly. Thanks for the suggestion!

@chalk

Thanks for the code and the python theory of
" # because children is a mutable list you need to
# append to it or extend it."

Hope you all have a great day ahead!

Thanks again!