The lambda functions in Python always seemed very cool to me, but I never thought that I would actually get to use them in my projects. Guess what? Last night I was working on a side project when I had the perfect opportunity to use and very much appreciate this feature!
As part of my move of this site to django I have to move over the “custom” textile format parsing that I created for this site. Basically, it’s mostly textile with the additional feature of allowing you to easily embed the attached image or file. Below is the Python specific regular expression that defines this syntax.
pre.
\[(?P
This allows me to easily reference an attached image and instructor the site to resize based on my needs. So, after I upload an image for a blog entry all I have to do is put [image:>0=thumb
] (right aligned “standard” thumbnail sized, as predefined by me) or [image:0=200x100
] (default alignment with 200×100 resize keeping the original image in proportion) wherever I want an embedded image to be displayed. With this I don’t have to worry about resizing the image or adding the img tag manually, all that is done automatically by my Node class.
Ruby has a useful notation where I could do this in a block notation as…
1 2 3 |
replaced = txt.gsub(/\[(inline|image):([<>]?)(\S+?)(=(.*?))?\]/m) { // do all the custom code in this block. } |
Since python doesn’t have the concept of blocks it uses the approach of passing in functions that return the replacement text.
1 2 3 4 5 6 7 8 |
def replacement_function(match): // interpret the match in this function return "...replacement for match." def interpret_attachments(text): attach = re.compile(r'\[(?P<type>inline|image):(?P<alignment>[<>]?)(?P<positionOrAlias>\S+?)(=(?P<size>.*?))?\]', re.MULTILINE | re.IGNORECASE) replaced = attach.sub(replacement_function, text) return replaced |
Now, the problem comes in if replacement_function needs some extra parameter, which in my case was node_id. How can you pass that to replacement_function?
1 2 3 |
def replacement_function(node_id, match): // interpret the match in this function return "...replacement for match." |
That is where lambda function comes in. I ended up encapsulating the call in a lambda function and passing that to the sub() function.
1 2 3 4 |
def interpret_attachments(text, node_id): attach = re.compile(r'\[(?P<type>inline|image):(?P<alignment>[<>]?)(?P<positionOrAlias>\S+?)(=(?P<size>.*?))?\]', re.MULTILINE | re.IGNORECASE) replaced = attach.sub(lambda match: get_replacement(node_id, match), text) return replaced |
I remembered seeing numerous references to lambda functions in the django framework, so now that I understood the need for these, I went back and looked at a few examples. They all make much more sense now!
Here is an excerpt from django/contrib/auth/decorators.py
1 2 3 4 5 6 |
def permission_required(perm, login_url=None): """ Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page if necessary. """ return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url) |
…and one more example from django/template/defaultfilters.py
1 2 3 4 5 |
def title(value): """Converts a string into titlecase.""" return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) title.is_safe = True title = stringfilter(title) |
Awesome!
If you are confused about lambda functions then I would suggest reading through the filter, map and reduce section on the python site. That section demonstrates good usage of lambda functions.