We've Moved


The blog has been retired - it's up for legacy reasons, but these days I'm blogging at blog.theodox.com. All of the content from this site has been replicated there, and that's where all of the new content will be posted. The new feed is here . I'm experimenting with crossposting from the live site, but if you want to keep up to date use blog.theodox.com or just theodox.com

Saturday, January 10, 2015

Dang

You know the old saying, "you learn something new every day?" Well it's true. Usually, it's something like "I don't know where I left my keys," but sometimes you run into something that you realized you should have known all along and yet somehow it takes you by surprise.


Here's a little nugget that I stumbled onto today.  If you know Maya, you probably know that cmds.ls() with no arguments gives you a list of every entity in your current maya scene.  However if you pass in a list, ls() will filter it down.  It's very common to do something like

stuff = ['pCubeShape1', 'top', 'persp']
cmds.ls(stuff, type = 'camera')

as a cheap way of filtering a list of objects by type, or

cmds.ls(stuff, l = True)

to get long names and so on.  All pretty 101.

Now, if you're an old Pythonista, you've probably tried it like this too:

cmds.ls(*stuff, l = True)

and gotten the same result.  Usually, *args is a great help in writing simpler code, since you write functions that take an arbitrary number of arguments without forcing the callers to create lists or tuples. Your code can use loops or comprehensions knowing that the *args will be iterable even if it's empty:

def starargs(*args):
    for idx, item in enumerate (args):
        print idx, '\t', item


starargs() # prints nothing

starargs('a')
# 0  a

starargs('i', 'j', 'k')
# 0  i
# 1  j
# 2 k

starargs(*['x','y','z'])
# 0    x
# 1    y
# 2    z

Unfortunately this nice behavior can bite you if you use it with cmds.ls().  It's easy to miss the difference between

cmds.ls(stuff)

and 

cmds.ls(*stuff)

especially because most functions will treat these interchangeably.

However (!) the no arguments means list everything behavior means that the first one returns and empty list, but the second returns a list of everything in Maya.  If you were using the ls() as a filter or a long-name-converter you are likely to be very surprised by the results. I was using it as part of a cleanup routine, and I suddenly discovered I was 'cleaning' everything in my scenes.

Like I said, you learn something new everyday -- in this case, new curse words!

You can work around it simply enough by not passing *args to ls(), or at least by not doing so without checking if the argument is valid:


def list_xforms (*args):
      if not args: return []
      return cmds.ls(*args, type='transform')

Not an earth-shaking discovery, just another one of the many mysteries of the Maya.