Search code examples
pythonfunctionlambdatypesfunction-declaration

How to find out if a function has been declared by `lambda` or `def`?


If I declare two functions a and b:

def a(x):
    return x**2

b = lambda x: x**2

I can not use type to differentiate them, since they're both of the same type.

assert type(a) == type(b)

Also, types.LambdaType doesn't help:

>>> import types
>>> isinstance(a, types.LambdaType)
True
>>> isinstance(b, types.LambdaType)
True

One could use __name__ like:

def is_lambda_function(function):
    return function.__name__ == "<lambda>"

>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True

However, since __name__ could have been modified, is_lambda_function is not guaranteed to return the correct result:

>>> a.__name__ = '<lambda>'
>>> is_lambda_function(a)
True

Is there a way which produces a more reliable result than the __name__ attribute?


Solution

  • AFAIK, you cannot reliably in Python 3.

    Python 2 used to define a bunch of function types. For that reason, methods, lambdas and plain functions have each their own type.

    Python 3 has only one type which is function. There are indeed different side effects where declaring a regular function with def and a lambda: def sets the name to the name (and qualified name) of the function and can set a docstring, while lambda sets the name (and qualified name) to be <lambda>, and sets the docstring to None. But as this can be changed...

    If the functions are loaded from a regular Python source (and not typed in an interactive environment), the inspect module allows to access the original Python code:

    import inspect
    
    def f(x):
        return x**2
    
    g = lambda x: x**2
    
    def is_lambda_func(f):
        """Tests whether f was declared as a lambda.
    
    Returns: True for a lambda, False for a function or method declared with def
    Raises:
        TypeError if f in not a function
        OSError('could not get source code') if f was not declared in a Python module
                                             but (for example) in an interactive session
    """
        if not inspect.isfunction(f):
            raise TypeError('not a function')
        src = inspect.getsource(f)
        return not src.startswith('def') and not src.startswith('@') # provision for decorated funcs
    
    g.__name__ = 'g'
    g.__qualname__ = 'g'
    
    print(f, is_lambda_func(f))
    print(g, is_lambda_func(g))
    

    This will print:

    <function f at 0x00000253957B7840> False
    <function g at 0x00000253957B78C8> True
    

    By the way, if the problem was serialization of function, a function declared as a lambda can successfully be pickled, provided you give it a unique qualified name:

    >>> g = lambda x: 3*x
    >>> g.__qualname__ = "g"
    >>> pickle.dumps(g)
    b'\x80\x03c__main__\ng\nq\x00.'