what is it?

an entity that can be dynamically created, destroyed, passed to a function, returned as a value, and have all the rights as other variables in the programming language have

just summarising this blog post

can create a function and assign it to another variable

def yell(text):
    return text.upper() + '!'
 
>>> yell('hello')
'HELLO!'
 
>>> bark = yell
 
>>> bark('woof')
'WOOF!'

can store in datastructure

>>> funcs = [bark, str.lower, str.capitalize]
>>> funcs
[<function yell at 0x10ff96510>,
 <method 'lower' of 'str' objects>,
 <method 'capitalize' of 'str' objects>]
 
>>> funcs[0]('heyho')
'HEYHO!'

can pass function to other functions

def greet(func):
    greeting = func('Hi, I am a Python program')
    print(greeting)
 
>>> greet(yell)
'HI, I AM A PYTHON PROGRAM!'

functions that can accept other functions as arguments are also called higher-order functions

can be nested

def speak(text):
    def whisper(t):
        return t.lower() + '...'
    return whisper(text)
 
>>> speak('Hello, World')
'hello, world...'

whisper() only exists in speak()

>>> whisper('Yo')
NameError: "name 'whisper' is not defined"
 
>>> speak.whisper
AttributeError: "'function' object has no attribute 'whisper'"

can capture local state

lexical closures remember the values from its enclosing lexical scope even when the program flow is no longer in that scope

def make_adder(n):
    def add(x):
        return x + n
    return add
 
>>> plus_3 = make_adder(3)
>>> plus_5 = make_adder(5)
 
>>> plus_3(4)
7
>>> plus_5(4)
9

objects can behave like functions

if an object is callable it means you can use round parentheses () on it and pass function call arguments to it

class Adder:
    def __init__(self, n):
         self.n = n
    def __call__(self, x):
        return self.n + x
 
>>> plus_3 = Adder(3)
>>> plus_3(4)
7

callable is a built-in python function to check whether an object can called

>>> callable(plus_3)
True
>>> callable(yell)
True
>>> callable(False)
False