Функции как равноправные объекты

In [1]:
def f():
    print(1)
    
g = f
g()
1

svn.py

In [2]:
import sys


def checkout(*args):
    print('checkout{}'.format(args))

def commit(*args):
    print('commit{}'.format(args))


commands = {
        'checkout': checkout,
        'co': checkout,
        'commit': commit,
        'ci': commit
        }

cmd, *args = 'commit', 'arg1', 'arg2' #sys.argv[1:]
commands[cmd](*args)
commit('arg1', 'arg2')

Передача функций в качестве аргументов в другие функции

Аналог filter

In [1]:
def filter(function, iterable):
    for e in iterable:
        if function(e):
            yield e

def is_even(number):
    return number % 2 == 0


a = [1, 2, 3, 4, 5]
for e in filter(is_even, a):
    print(e)
2
4
In [2]:
a = range(6)
for e in filter(is_even, a):
    print(e)
0
2
4

Неполный аналог map

In [1]:
def map(function, iterable):
    for e in iterable:
        yield function(e)
        
def square(x):
    return x ** 2


for e in map(square, [1, 2, 3]):
    print(e)
1
4
9

Аналог map

In [2]:
def map(function, *iterables):
    iters = [iter(iterable) for iterable in iterables]
    while True:
        try:
            args = [next(it) for it in iters]
            yield function(*args)
        except StopIteration:
            break
            
def add(a, b):
    return a + b


for e in map(add, [1, 2, 3], [4, 5, 6]):
    print(e)
5
7
9

lambda

In [1]:
for e in map(lambda a, b: a + b, [1, 2, 3], [4, 5, 6]):
    print(e)
5
7
9

Функции, которые возвращают другие функции

In [1]:
def add(a, b):
    return a + b

def partial(function, *args):    
    def new_function(*more_args):
        all_args = args + more_args
        return function(*all_args)
    return new_function
    
inc = partial(add, 1)
inc(4)
Out[1]:
5
In [2]:
import functools
import operator


inc = functools.partial(operator.add, 1)
print(inc(4))
5

Кеширующий декоратор

In [1]:
import timeit

def caching(function):
    computed_results = {}
    def new_function(*args):
        if args not in computed_results:
            computed_results[args] = function(*args)
        return computed_results[args]
    return new_function


def fibonacci_number(i):
    if i == 0 or i == 1:
        return 1
    return fibonacci_number(i - 1) + fibonacci_number(i - 2)

print(timeit.timeit('fibonacci_number(30)', number=3, setup='from __main__ import fibonacci_number'))
fibonacci_number = caching(fibonacci_number)
print(timeit.timeit('fibonacci_number(30)', number=3, setup='from __main__ import fibonacci_number'))
2.3664230789872818
5.887000588700175e-05
In [2]:
import timeit

def caching(function):
    computed_results = {}
    def new_function(*args):
        if args not in computed_results:
            computed_results[args] = function(*args)
        return computed_results[args]
    return new_function


@caching
def fibonacci_number(i):
    if i == 0 or i == 1:
        return 1
    return fibonacci_number(i - 1) + fibonacci_number(i - 2)

print(timeit.timeit('fibonacci_number(30)', number=3, setup='from __main__ import fibonacci_number'))
5.275104194879532e-05

Что можно узнать о функции?

In [1]:
def f(x):
    print(x)
    
print(f.__name__)
g = f
print(g.__name__)
g = lambda: 1
print(g.__name__)
print(f.__module__)
f
f
<lambda>
__main__

docstrings

In [2]:
def caching(function):
    '''Caching decorator for functions with positional arguments.'''

    computed_results = {}
    def new_function(*args):
        if args not in computed_results:
            computed_results[args] = function(*args)
        return computed_results[args]
    new_function.__name__ = function.__name__
    new_function.__doc__ = function.__doc__
    return new_function

@caching
def fibonacci_number(i):
    '''Function for calculating i-th Fibonacci number.'''

    if i == 0 or i == 1:
        return 1
    return fibonacci_number(i - 1) + fibonacci_number(i - 2)
In [3]:
import caching
help(caching) #    $ pydoc caching
Help on module caching:

NAME
    caching

FUNCTIONS
    caching(function)
        Caching decorator for functions with positional arguments.
    
    fibonacci_number(*args)
        Function for calculating i-th Fibonacci number.

FILE
    /Users/tswr/python/caching.py


In [4]:
from IPython.display import HTML
with open('caching.html') as f:  #   $ pydoc -w caching
    h = HTML(f.read())
h
Out[4]:
Python: module caching
 
 
caching
index
/Users/tswr/python/caching.py

 
Functions
       
caching(function)
Caching decorator for functions with positional arguments.
fibonacci_number(*args)
Function for calculating i-th Fibonacci number.

code object

In [5]:
caching.caching.__code__.co_code
Out[5]:
b'i\x00\x00\x89\x00\x00\x87\x00\x00\x87\x01\x00f\x02\x00d\x01\x00d\x02\x00\x86\x00\x00}\x01\x00\x88\x01\x00j\x00\x00|\x01\x00_\x00\x00\x88\x01\x00j\x01\x00|\x01\x00_\x01\x00|\x01\x00S'
In [6]:
import dis
dis.dis(caching.caching)
  4           0 BUILD_MAP                0
              3 STORE_DEREF              0 (computed_results)

  5           6 LOAD_CLOSURE             0 (computed_results)
              9 LOAD_CLOSURE             1 (function)
             12 BUILD_TUPLE              2
             15 LOAD_CONST               1 (<code object new_function at 0x10eb93420, file "/Users/tswr/python/caching.py", line 5>)
             18 LOAD_CONST               2 ('caching.<locals>.new_function')
             21 MAKE_CLOSURE             0
             24 STORE_FAST               1 (new_function)

  9          27 LOAD_DEREF               1 (function)
             30 LOAD_ATTR                0 (__name__)
             33 LOAD_FAST                1 (new_function)
             36 STORE_ATTR               0 (__name__)

 10          39 LOAD_DEREF               1 (function)
             42 LOAD_ATTR                1 (__doc__)
             45 LOAD_FAST                1 (new_function)
             48 STORE_ATTR               1 (__doc__)

 11          51 LOAD_FAST                1 (new_function)
             54 RETURN_VALUE
In [7]:
import inspect
sig = inspect.signature(caching.caching)
print(sig)
(function)
In [8]:
print(sig.parameters)
OrderedDict([('function', <Parameter at 0x10eb7e1f8 'function'>)])
In [9]:
print(inspect.getsource(caching.caching))
def caching(function):
    '''Caching decorator for functions with positional arguments.'''

    computed_results = {}
    def new_function(*args):
        if args not in computed_results:
            computed_results[args] = function(*args)
        return computed_results[args]
    new_function.__name__ = function.__name__
    new_function.__doc__ = function.__doc__
    return new_function