If you are looking for dynamic code execution, callback validation, decorator creation, and plugin systems, you need to check whether a variable you are operating on is a function or not.
Functions in Python are first-class objects. A variable can be a built-in function, an object method, a user-defined function, a lambda function, or a callable method.
Method 1: Using callable()
The pragmatic and efficient way to check if a variable is a function in Python is to use the callable(). The callable() function checks if an object can be called like a function.
def user_defined_func():
return "Stranger Things 5"
class CallableClass:
def __call__(self):
return "I'm callable!"
print(callable(user_defined_func))
# Output: True
print(callable(lambda x: x))
# Output: True
print(callable(CallableClass()))
# Output: True (has __call__)
print(callable(print))
# Output: True (built-in)
print(callable("string"))
# Output: False
print(callable(42))
# Output: False
In the code above, we used the callable() method on a user-defined function, a lambda function, a CallableClass, and a built-in function, and each returned True.
Now, the string and integers are not functions. So, it returns False.
Method 2: Using inspect.isfunction()
What if you only want to detect user-defined functions, not built-in functions? In that case, use the inspect.isfunction() method.
import inspect
def user_defined_function():
pass
class MyClass:
def method(self):
pass
# Strict user-defined function checking
print(inspect.isfunction(user_defined_function))
# Output: True
print(inspect.isfunction(lambda x: x))
# Output: True
print(inspect.isfunction(MyClass.method))
# Output: True (unbound)
print(inspect.isfunction(MyClass().method))
# Output: False (bound method)
print(inspect.isfunction(print))
# Output: False (built-in)
The main advantage of this approach is that it can easily differentiate user-defined functions from other types. It is a specific approach used for a specific case.
Method 3: Using isinstance()
With the help of the isinstance() function, we can verify a variable’s data type is a function with types.FunctionType. The “types” is a built-in module that you can import.
If the output of the isinstance() method is True, it is a function; False otherwise.
For built-in function type, use the types.BuiltinFunctionType.
import types
def user_defined_func():
pass
print(isinstance(user_defined_func, types.FunctionType))
# Output: True
print(isinstance(lambda x: x, types.FunctionType))
# Output: True
print(isinstance(print, types.BuiltinFunctionType))
# Output: True
print(type(user_defined_func) == types.FunctionType)
# Output: True (exact type)
This is a type-based approach, which is expressive and explicitly readable.
Method 4: Checking __call__ Attribute
If a variable is a function, it must have __call__ attribute. So, the main logic behind this approach is that if we verify whether a variable has __call__ attribute, we can confirm that it is a function, otherwise not.
You can use the built-in hasattr() function to check if a variable has __call__ attribute.
def user_defined_func():
pass
class CustomCallable:
def __call__(self):
pass
print(hasattr(user_defined_func, '__call__'))
# Output: True
print(hasattr(CustomCallable(), '__call__'))
# Output: True
print(hasattr("string", '__call__'))
# Output: False
In the first two code examples, those variables are functions because they possess the __call__ attribute.
String does not have a __call__ attribute. So, it returns False.
That’s all!

