inspect
module provides functions that determine whether objects are methods, functions, classes, modules, etc. However, there is no method that tells you whether something is a lambda expression. The isfunction()
is probably close enough for most applications, but believe it or not, I recently encountered a case where it made sense to issue a warning if an argument was a lambda expression.Here is a python function that determines whether or not its argument is a lambda expression:
import inspect
def islambda(f):
return inspect.isfunction(f) and \
f.__name__ == (lambda: True).__name__
Currently, the
__name__
of anonymous functions created by lambda
is "<lambda>", so I could have just hard-coded that string into the comparison. But I chose to use the (lambda: True).__name__
expression instead just in case python uses a different name for lambda expressions in the future.This function works as expected in all common cases:
>>> islambda(lambda: 1)
True
>>> islambda(islambda)
False
>>> islambda(globals)
False
>>> islambda(str)
False
>>> islambda(str.join)
False
>>> islambda("".join)
False
The only case that I am aware of where it will not work is if you carefully craft a function with the name "<lambda>":
import new
>>> x = new.function(
compile("print 'Hello World!'", "<string>", "exec"),
{}, '<lambda>')
>>> islambda(x)
True
Consider yourself warned. :)
In case you are curious, the application I was working on had a method that took a callable as an argument and held a weak reference to it. I would have loved to have been able to issue a warning anytime a callable was passed that would "immediately" be garbage collected before anything useful was done, but that is a non-trivial condition to detect (hint: it involves reading the programmer's mind). But there is a common subset of that error case that is relatively easy to detect: callers passing their only reference to a lambda expression. That case can be trivially detected using the
islambda()
function described above along with the sys.getrefcount()
function like so:
import sys
from warnings import warn
...
def myfunc(f):
if sys.getrefcount(f) == 3 and islambda(f):
warn('f is too short-lived to be useful', stacklevel=2)
...
Since it is not obvious, I should point out that (in this example) a reference count of 3 indicates that
myfunc()
's caller holds no references to the callable f
. The reason is that sys.getrefcount()
will hold one reference, the name f
is bound to one reference, and there is a temporary reference held by the python interpreter across the call to myfunc()
, so if sys.getrefcount()
returns 3, we know those are the only three references.Incidentally, the fact that
islambda()
erroniously identifies a function with the same "<lambda>" as a lambda expression is inconsequential for my stated purpose: if the crafted function has no other references, I want to issue a warning just the same as if it had truly been a lambda expression.Which brings me back to
isfunction()
. It turns out, not surprisingly, that isfunction()
is sufficient for my needs since a function with only 3 references has, by definition, no external references. In the end, I didn't actually use my islambda()
function and went with isfunction()
for my application instead:
import inspect
import sys
from warnings import warn
...
def myfunc(f):
if sys.getrefcount(f) == 3 and inspect.isfunction(f):
warn('f is too short-lived to be useful', stacklevel=2)
...
This handles both lambda expressions and functions dynamically created using the
new
module.
No comments:
Post a Comment