[NCLUG] help w/ python list comprehension
Sean Reifschneider
jafo at tummy.com
Tue Aug 27 20:29:54 MDT 2013
On 08/21/2013 09:54 AM, Mike Cullerton wrote:
> for item in items:
> missing = False
> for tag in tags:
> # do we have a tag (ignore tag == '')
> # can we find it in item.tags
> if tag and not tag.lower() in [tag.lower() for tag in item.tags]:
> missing = True
> break
> if not missing: items_by_tag.append(item)
> return items_by_tag
One thing I'd recommend against is the duplicate use of tag in the
comprehension:
if tag and not tag.lower() in [tag.lower() for tag in item.tags]:
Instead, I tend to use "x" for things that are temporary in a
comprehension:
if tag and not tag.lower() in [x.lower() for x in item.tags]:
That reduces any confusion about the multiple uses of tag.
You're also computing the item.tags multiple times, so it might be more
obvious to make it:
for item in items:
item_tags_lower = [x.lower() for x in item.tags]
missing = False
for tag in tags:
# do we have a tag (ignore tag == '')
# can we find it in item.tags
if tag and not tag.lower() in item_tags_lower:
missing = True
break
if not missing: items_by_tag.append(item)
return items_by_tag
One place you can use a list comprehension here is to get rid of the "if
tag":
for item in items:
item_tags_lower = [x.lower() for x in item.tags]
missing = False
for tag in [x for x in tags if x]:
if not tag.lower() in item_tags_lower:
missing = True
break
if not missing: items_by_tag.append(item)
return items_by_tag
But you're also re-computing the tag.lower() as well, so:
tags_lower = [x.lower() for x in tags if x]:
for item in items:
item_tags_lower = [x.lower() for x in item.tags]
missing = False
for tag in tags_lower:
if not tag in item_tags_lower:
missing = True
break
if not missing: items_by_tag.append(item)
return items_by_tag
But, I believe what you're trying to do here is to return a list of items
that have a tag in the list of tags, right? This is something that sets
can help us with, because we can use logic operators on sets:
>>> set([1,2,3]) & set([4,5,6])
set([])
>>> set([1,2,3]) & set([3,4,5])
set([3])
So:
tags_set = set([x.lower() for x in tags if x])
return [x for x in items if
set([x.lower() for x in item.tags]) & tags_set]
That has the benefit of not turning an iterator into a static list, if you
are dealing with a huge list of items, say from a database...
Is that, as Stephen says, less clear? Well, I'd say that the last example
above is much more clear than the original code. But it could probably be
made more clear with:
tags_set = set([x.lower() for x in tags if x])
def any_matching_tags(search):
return set([x.lower() for x in item.tags]) & tags_set
return [x for x in items if any_matching_tags(x)]
Sean
More information about the NCLUG
mailing list