1. Callable Objects
- Functions in Python are first-class objects. Integers, strings, and dictionaries are other examples of first-class objects in Python—nothing fancy here. Programming language researchers define a “first-class object” as a program entity that can be:
- Created at runtime
- Assigned to a variable or element in a data structure
- Passed as an argument to a function
- Returned as the result of a function
- The call operator () may be applied to other objects besides functions. To determine whether an object is callable, use the callable() built-in function. As of Python 3.9, the data model documentation lists nine callable types:
- User-defined functions: Created with def statements or lambda expressions
- Built-in functions: A function implemented in C (for CPython), like len or time.strftime
- Built-in methods: Methods implemented in C, like dict.get
- Methods: Functions defined in the body of a class
- Classes: When invoked, a class runs its __new__ method to create an instance, then __init__ to initialize it, and finally the instance is returned to the caller
- Class instances: If a class defines a __call__ method, then its instances may be invoked as functions
- Generator functions: Functions or methods that use the yield keyword in their body. When called, they return a generator object
- Native coroutine functions: Functions or methods defined with async def. When called, they return a coroutine object. Added in Python 3.5
- Asynchronous generator functions: Functions or methods defined with async def that have yield in their body. When called, they return an asynchronous generator for use with async for. Added in Python 3.6
- Generators, native coroutines, and asynchronous generator functions are unlike other callables in that their return values are never application data, but objects that require further processing to yield application data or perform useful work. Generator functions return iterators. Native coroutine functions and asynchronous generator functions return objects that only work with the help of an asynchronous programming framework, such as asyncio
1 | print([callable(obj) for obj in (abs, str, 'Ni!')]) # [True, True, False] |
1.1. Higher-Order Functions
- A function that takes a function as an argument or returns a function as the result is a higher-order function (e.g. map, filter, reduce, sorted)
1 | def factorial(n): |
1.2. Anonymous Functions
- The lambda keyword creates an anonymous function within a Python expression. The best use of anonymous functions is in the context of an argument list for a higher-order function
- However, the simple syntax of Python limits the body of lambda functions to be pure expressions. In other words, the body cannot contain other Python statements such as while, try, assignment with =, etc. The new assignment expression syntax using := can be used—but the lambda will be complicated and hard to read, and it should be refactored into a regular function
1 | fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana'] |
1.3. User-Defined Callable Types
- A class implementing __call__ is an easy way to create function-like objects that have some internal state that must be kept across invocations. Another good use case for __call__ is implementing decorators. Decorators must be callable, and it is sometimes convenient to “remember” something between calls of the decorator (e.g., for memoization—caching the results of expensive computations for later use) or to split a complex implementation into separate methods. The functional approach to creating functions with internal state is to use closures
1 | import random |
2. From Positional to Keyword-Only Parameters
- One of the best features of Python functions is the extremely flexible parameter handling mechanism. Closely related are the use of * and ** to unpack iterables and mappings into separate arguments when we call a function
1 | # name keyword-only arguments after the argument prefixed with * |
3. Packages for Functional Programming
- The operator module provides function equivalents for dozens of operators so you don’t have to code trivial functions
1 | # functions defined in operator |
References
- Fluent Python