Summaries/Python/FP_python/FP2-First-Class-and-Higher-...

9.6 KiB

First-Class and Higher-Order Functions

In [1]:
def add_numbers(x, y):
    return x + y


my_function = add_numbers
my_function(3, 4)
Out[1]:
7
In [2]:
# Example

mode = "PROD"


def load_data_real():
    print("loading data from a third-party API ...")
    return [100, 200, 300]


def load_data_test():
    return [1, 2, 3]


load_data = load_data_real if mode == "PROD" else load_data_fake
load_data()  # rest of code can use this function....
loading data from a third-party API ...
Out[2]:
[100, 200, 300]

Passing functions as an argument

In [3]:
def subtract_numbers(x, y):
    return x - y


def combine_numbers(x, y, func):
    return func(x, y)


print(combine_numbers(10, 20, subtract_numbers))
print(combine_numbers(10, 20, add_numbers))
-10
30

Return functions

In [20]:
def create_printer(arg):
    # arg is a use of closure
    def say_hello():
        print(f"hello from inside an other function {arg}")

    return say_hello


printer_1 = create_printer("ARG")
printer_1()

printer_2 = create_printer("XXXXXX")
printer_2()
hello from inside an other function ARG
hello from inside an other function XXXXXX

Examples Higher order functions

In [31]:
def create_sales_calculator(percentage_discount):
    def calculator(price):
        return price * (1 - percentage_discount)
    
    return calculator
    
discount = 0.20
price = 220
final_price = create_sales_calculator(discount)(price)
print(final_price)
176.0

auto print which function is invoked

Decorators

In [23]:
def create_spy(function_name):
    def inner(func):
        def spy_func(arg):
            print(f"The {function_name} was called")
            return func(arg)

        return spy_func

    return inner


@create_spy("Double")
def double(x):
    return x * 2


@create_spy("Square")
def square(x):
    return x * x


@create_spy("Greet")
def greet(name):
    print(f"Hello {name}")


# double(square(10))
# greet('Paul')
double(9)
The Double was called
Out[23]:
18

test performance of a function; find bottlenecks

In [24]:
import datetime


def add_performance_watch(func):
    def inner(*args, **kwargs):
        start = datetime.datetime.now()
        result = func(*args, **kwargs)
        print((kwargs))
        end = datetime.datetime.now()
        total_time = end - start
        print(f"The {func.__name__} functions executed in {total_time}")
        return result

    return inner


@add_performance_watch
@create_spy("Double")
def double(x):
    return x * 2


@add_performance_watch
@create_spy("Square")
def square(x):
    return x * x


@add_performance_watch
@create_spy("Greet")
def greet(name):
    print(f"Hello {name}")


double(square(9))
The Square was called
{}
The spy_func functions executed in 0:00:00.000023
The Double was called
{}
The spy_func functions executed in 0:00:00.000005
Out[24]:
162

Isomorphic funtions

In [35]:
def list_or_value(func):
    def wrapper(arg):
        if isinstance(arg, list):
            return list(map(func, arg))
        else:
            return func(arg)

    return wrapper


@list_or_value
def double(x):
    return x * 2


@list_or_value
def minus_one(x):
    return x - 1


numbers = list(range(1, 6))

print(double(9))
print(minus_one(9))

print(double(numbers))
print(minus_one(numbers))
18
8
[2, 4, 6, 8, 10]
[0, 1, 2, 3, 4]

Function tracking

In [45]:
import datetime


def print_stats(func):
    def wrapper(*args, **kwargs):
        start = datetime.datetime.now()
        result = func(*args, **kwargs)
        end = datetime.datetime.now()
        total_time = end - start

        stats = {
            "name": func.__name__,
            "args": args,
            "kwargs": kwargs,
            "result": result,
            "total_time": total_time,
        }
        print(stats)

    return wrapper


@print_stats
def double(x):
    return x * 2


@print_stats
def add(x, y, z):
    return x + y + z


@print_stats
def greet(x):
    return x


double(9)
add(1, 2, z=9)
greet(("Hello"))
{'name': 'double', 'args': (9,), 'kwargs': {}, 'result': 18, 'total_time': datetime.timedelta(microseconds=3)}
{'name': 'add', 'args': (1, 2), 'kwargs': {'z': 9}, 'result': 12, 'total_time': datetime.timedelta(microseconds=2)}
{'name': 'greet', 'args': ('Hello',), 'kwargs': {}, 'result': 'Hello', 'total_time': datetime.timedelta(microseconds=2)}

Argument checking

In [ ]: