- Practical Maya Programming with Python
- Robert Galanakis
- 856字
- 2021-09-03 10:05:26
Learning to use list comprehensions
Let's look at list comprehensions in depth now that we've used simpler versions several times. It's a powerful-yet-simple syntax that has been in Python since version 2.0. List comprehensions are described very succinctly and with very clear examples in PEP 202 at http://www.python.org/dev/peps/pep-0202/.
Tip
A Python Enhancement Proposal (PEP) is a design document for a Python feature (or similar). Often it can be very long and technical. PEP 202 is a very straightforward PEP that is well worth reading, consisting of three paragraphs and one example block. Some other notable PEPs will be mentioned in this book and in the appendices.
Oh, and PEP 1 describes PEPs and the PEP process, of course.
A list comprehension has the following form:
[<map> for <variable> in <selection> | if <predicate>]
The map
is the item that ends up in the list. It can be the same as variable
if no transformation is to take place. It's commonly a function that transforms variable
. The variable
is the item in selection
being iterated over. The selection
is the sequence being iterated. The predicate
is optional expression, but is a function that takes variable
and returns True
if the item should be included in the result or False
if not. For example, the following list comprehension generates a list of uppercase letters from an input string:
>>> s = 'hi!' >>> [c.upper() for c in s if c.isalpha()] ['H', 'I']
In the preceding example:
s
is theselection
.c
is thevariable
.c.alpha()
is thepredicate
.c.upper()
is themap
.
Let's look at some more simple examples.
>>> [i + 1 for i in [1, 2, 3]] [2, 3, 4] >>> [i for i in [1, 2, 3] if i > 2] [3] >>> [(i, chr(i)) for i in [65, 66, 67]] [(65, 'A'), (66, 'B'), (67, 'C')]
You can also nest list comprehensions, but we'll avoid that for this book. PEP 202 includes many examples of nested list comprehensions, and they can be very useful. However, only attempt them once you are comfortable with regular list comprehensions.
List comprehensions are vital when writing composable code, and we'll use them repeatedly throughout the rest of the book. If you're still uncomfortable, fire up a Python interpreter and get comfortable!
Implementing is_exact_type
With our experience building composable code and using list comprehensions, let's take a stab at creating an is_exact_type
function that will replace the need for the exactType
parameter in Maya's listing functions.
First, we need to choose a place to put the function. The minspect.py
file we created in Chapter 1, Introspecting Maya, Python, and PyMEL, is a reasonable location for it. Let's put the new is_exact_type
function there.
def is_exact_type(node, typename):"""node.type() == typename"""return node.type() == typename
The is_exact_type
function is very simple, checking whether two type strings are equal.
We can add more predicates just as easily. The following is_type
function returns True
if the type of node
has a MEL type of typename
or a subclass.
def is_type(node, typename): """Return True if node.type() is typename or any subclass of typename.""" return typename in node.nodeType(inherited=True)
Let's look at some examples that use our new predicates. Notice how is_exact_type
matches only the camera node, but is_type
finds the joint, sphere transform, and camera nodes.
>>> objs = pmc.joint(), pmc.polySphere(), pmc.camera() >>> [o for o in pmc.ls() if minspect.is_exact_type(o, 'camera')] [...nt.Camera(u'cameraShape1')] >>> [o for o in pmc.ls() if minspect.is_type(o, 'transform')] [...nt.Joint(u'joint1'), nt.Transform(u'pSphere1'), nt.Transform(u'camera1')]
When a list comprehension uses a simple predicate, we may not even want to create a function for it. We can define our predicate expression right in the comprehension. The following code selects all joints with a positive x translation:
>>> j1, j2 = pmc.joint(), pmc.joint() >>> j2.translateX.set(10) >>> [j for j in pmc.ls(type='joint') if j.translateX.get() > 0] [nt.Joint(u'joint2')]
Though this code may be less familiar for a MEL guru, it is much more easily understandable for anyone used to Python. And more importantly, it is far easier to maintain, and especially compose.
Saying goodbye to map and filter
Instead of list comprehensions, it used to be popular to use the built-in map
and filter
functions. For example, the two preceding examples could be written as follows:
>>> map(lambda i: i + 1, [1, 2, 3]) [2, 3, 4] >>> filter(lambda i: i > 2, [1, 2, 3]) [3]
In some cases, the map
and filter
form can actually be more concise than the list comprehension form, as demonstrated by finding all root joints.
>>> filter(is_root_joint, pmc.ls()) [nt.Joint(u'joint1')] >>> [o for o in pmc.ls() if is_root_joint(o)] [nt.Joint(u'joint1')]
In full disclosure, I sometimes use map
and filter
when it's more concise. But as a rule, use list comprehensions. We'll use list comprehensions exclusively instead of map
and filter
in this book, to reduce confusion and enforce style.
If you're interested in why this book and the Python community at large encourages list comprehensions of map
and filter
, a simple Internet search should turn up lots of good reading. List comprehensions are now Pythonic and map
and filter
are not.
Tip
Refer to Chapter 1, Introspecting Maya, Python, and PyMEL, for a discussion of what Pythonic means.