### Execute example CLI commands Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Executes the `run_example` utility with the predefined arguments to demonstrate the functionality of the 'hello' and 'goodbye' commands. ```python run_example(runner) ``` -------------------------------- ### TodoClient Initialization Example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Demonstrates how to instantiate the `TodoClient` with a base URL and a null logger for simplified logging. ```python client = TodoClient(BASE_URL, logger=null_logger) client ``` -------------------------------- ### Using Configured GET Request with Logging Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Demonstrates how to create and use a configured GET request function (`rget`) that includes a custom logger (`simple_logger`). It shows a call that triggers the logging. ```python # Assuming BASE_URL, DEFAULT_HEADER, default_error_handler, to_todo, and compose are defined # rget = to_get(BASE_URL, headers=DEFAULT_HEADER, error_handler=default_error_handler, logger=simple_logger) # rget('todos/1', transform=to_todo) ``` -------------------------------- ### Example Usage: Top 3 'R' Users Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates the usage of the `to_pipeline` function to retrieve the top 3 users whose names start with 'R'. It assumes `to_users()` provides the user data and `filter_user_by_name('R')` creates the filter. ```python list(to_pipeline(to_users(), filter_user_by_name('R'), 3)) ``` -------------------------------- ### Function Composition Example (Initial) Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Shows a verbose, non-composed way of applying multiple functions sequentially to a set of values. This example highlights the need for function composition to reduce boilerplate. ```python def squarer(a): return a * a def doubler(a): return 2 * a def incrementer(a): return a + 1 def forgetting_basics_of_programming(): # This is terrible for so many reasons a = squarer(doubler(1)) b = squarer(doubler(2)) c = squarer(doubler(3)) abc = [a,b,c] return abc forgetting_basics_of_programming() ``` -------------------------------- ### Logger Setup Functions Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Provides null and actual implementations for setting up logging, allowing for flexibility in CLI applications. The `setup_logger` function mocks the logging process. ```python def null_setup_logger(level:str, output_file:Optional[str]=None) -> None: pass def setup_logger(level:str, output_file:Optional[str]=None): # implementation omitted print("mock setting up logger level={} file={}".format(level, output_file)) ``` -------------------------------- ### Example Usage: Top 10 'S' Users Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates the usage of the `to_pipeline` function to retrieve the top 10 users whose names start with 'S'. It assumes `to_users()` provides the user data and `filter_s_users` is a pre-defined filter. ```python list(to_pipeline(to_users(), filter_s_users, 10)) ``` -------------------------------- ### Instantiate GET Request Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Creates an instance of the `to_get` closure, `rget`, configured with the `BASE_URL`. ```python rget = to_get(BASE_URL) ``` -------------------------------- ### Argparse CLI setup Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Defines functions for handling 'hello' and 'goodbye' commands and a function to create an argument parser using Python's `argparse` module. It sets up subparsers for different commands and associates default functions with them. ```python def hello(args): print('Hello, {0}!'.format(args.name)) def goodbye(args): print('Goodbye, {0}!'.format(args.name)) def get_parser(version="0.1.0"): parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version=version) subparsers = parser.add_subparsers() hello_parser = subparsers.add_parser('hello') hello_parser.add_argument('name') # add the name argument hello_parser.set_defaults(func=hello) # set the default function to hello goodbye_parser = subparsers.add_parser('goodbye') goodbye_parser.add_argument('name') goodbye_parser.set_defaults(func=goodbye) return parser def runner(argv): parser = get_parser() args = parser.parse_args(argv) return(args.func(args)) ``` -------------------------------- ### Comparison of Refactored Examples Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Compares the output of the `getting_better` and `to_better` functions, demonstrating the improved readability and conciseness achieved through function composition and list comprehensions. ```python getting_better(), to_better() ``` -------------------------------- ### CLI Argument Parser Setup Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Configures the main argument parser for the CLI, including version information and subparsers for different commands. It uses a helper function to add common options to subparsers. ```python def get_parser(version='0.1.0'): parser = argparse.ArgumentParser(description="Help for Tool") parser.add_argument('--version', action='version', version=version) subparsers = parser.add_subparsers() def _add_to_sp(name, add_opts, func): p = subparsers.add_parser(name) # For consistency we want the logging opts to be # added to all subparsers f = compose(add_opts, _add_opt_logging) _ = f(p) p.set_defaults(func=func) return p _add_to_sp('hello', add_opt_hello, run_hello) _add_to_sp('goodbye', _add_opt_name, run_goodbye) return parser ``` -------------------------------- ### Using Configured GET Request without Logging Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Illustrates creating a configured GET request function (`rget`) without specifying a logger, and then making a call to it. This call is expected not to produce any log output. ```python # Assuming BASE_URL, DEFAULT_HEADER, default_error_handler, to_todo, and compose are defined # rget = to_get(BASE_URL, headers=DEFAULT_HEADER, error_handler=default_error_handler) # # As expected, no logging # rget('todos/1', transform=to_todo) ``` -------------------------------- ### lru_cache: Cache Get Example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates the use of bound functions in Python's `lru_cache` to optimize dictionary lookups, a common pattern in performance-sensitive code. ```python cache_get = cache.get ``` -------------------------------- ### Reduce with Initial Value Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Illustrates using `functools.reduce` with an initial value. The reduction process starts with this provided value, allowing for calculations like cumulative sums starting from a specific number. ```python import functools import operator as op init_value = 11 functools.reduce(op.add, range(10), init_value) ``` -------------------------------- ### Fetch Todo Item with Default Headers Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Makes a GET request to fetch a 'todo' item, utilizing the default headers configured in the `rget` function. ```python rget('todos/1').json() ``` -------------------------------- ### Refactored Example with Composition Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Refactors the initial verbose example using the `compose` function to create a cleaner and more readable way to apply chained operations. It shows applying a composed function to individual values. ```python def squarer(a): return a * a def doubler(a): return 2 * a def getting_better(): f = compose(squarer, doubler) a = f(1) b = f(2) c = f(3) return [a, b, c] ``` -------------------------------- ### Partial Application: Filter Function Example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Illustrates a basic filter function that checks if a value is less than or equal to a minimum value, serving as a precursor to demonstrating `functools.partial`. ```python def to_filter(min_value): def func(x): return x <= min_value return func # Let's rewrite this in a more "natural" form. def filter_min_value(min_value, x): return x <= min_value for x, y in [(2, 4),(4, 3)]: print("min={} x={} {}".format(x, y, filter_min_value(x, y))) ``` -------------------------------- ### Getting the Number of Todos Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Illustrates how to get the count of todos retrieved from the service. ```python len(todos) ``` -------------------------------- ### Closure Example: Say Hello Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Demonstrates a basic closure where an outer function `say` returns an inner function `f` that remembers the `message` argument from its enclosing scope. ```python def say(message): def f(name): return " ".join([message, name]) return f say_hello = say("Hello") print(say_hello("Steve")) ``` -------------------------------- ### Define Default Headers and Instantiate Enhanced GET Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Defines a default header dictionary and then instantiates the enhanced `to_get` closure with the base URL and default headers. ```python DEFAULT_HEADER = {"x-my-header": "12345"} rget = to_get(BASE_URL, DEFAULT_HEADER) ``` -------------------------------- ### Create GET Request Function with Closure Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Defines a closure `to_get` that creates a function to make GET requests to a specified base URL. The returned function takes a URL segment and optional keyword arguments. ```python def to_get(base_url): def f(segment, **kwgs): url = "/".join([base_url, segment]) return requests.get(url) return f ``` -------------------------------- ### Function Composition and Partial Application in Python Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/README.md Demonstrates functional composition using `functools.reduce` and `reversed` to create a `compose` function. It also shows the use of `functools.partial` to create specialized functions like `add_one` and `add_two` from a base `adder` function. The example illustrates chaining these functions together. ```python import functools def compose(*funcs): """Functional composition [f, g, h] will be h(g(f(x))) """ def compose_two(f, g): def c(x): return f(g(x)) return c return functools.reduce(compose_two, reversed(funcs)) def adder(a: int, b: int) -> int: return a + b def multiply(n: int): def wrapper(m: int) -> int: return m * n return wrapper add_one = functools.partial(adder, 1) add_two = functools.partial(adder, 2) f = compose(add_one, multiply(3), add_two) assert f(7) == 26 fp = compose(f, print) fp(7) ``` -------------------------------- ### Calculating distance to origin Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb A standalone example of the `to_distance` function calculating the Euclidean distance from a point to the origin. ```python import math def to_distance(x, y, x0=0, y0=0): def f(a, a0): return math.pow(a - a0, 2) return math.sqrt(f(x, x0) + f(y, y0)) print(to_distance(3, 4)) ``` -------------------------------- ### Configurable GET Request Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb A higher-order function `to_get` that creates a specialized GET request function. It allows configuration of base URL, default headers, custom error handlers, and logging functions, integrating them into a response pipeline. ```python import requests import datetime def to_get(base_url, headers=None, error_handler=None, logger=None): def f(segment, **kw): url = "/".join([base_url, segment]) h = kw.get('headers', {}) headers.update(h) kw['headers'] = headers transform_func = kw.pop('transform', null_tranform) # Assuming null_tranform is defined elsewhere err_handler = null_tranform if error_handler is None else error_handler logger_func = null_tranform if logger is None else logger # Building custom response pipeline processor transform = compose(transform_func, err_handler, logger_func) return transform(requests.get(url, **kw)) return f ``` -------------------------------- ### Reduce for Finding Max Age User Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Provides an example of using `functools.reduce` to find a user with the maximum age from a list of users. It highlights the use of generators and O(1) space complexity. ```python import functools def reduce_max_age_user(acc, value): if value.age < acc.age: return acc return value functools.reduce(reduce_max_age_user, to_users()) ``` -------------------------------- ### Filter User by Name Prefix Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb A higher-order function that returns a user filtering function. The returned function checks if a user's first name starts with a given character. ```python def filter_user_by_name(achar): def func(user): return user.first_name.startswith(achar) return func ``` -------------------------------- ### Python map function example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates the basic usage of the `map` function to apply a squaring function to elements of an iterable. `map` returns an iterable, allowing for composition. ```python def f(x): return x * x XS = [1, 2, 3, 4, 5] rx = map(f, XS) print(rx) ``` -------------------------------- ### Python 2.7 Closure Workaround with Mutable Container Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Provides a Python 2.7 compatible example of simulating `nonlocal` behavior using a mutable container (list) within a closure to manage state. ```python def two_seven(n): # DONT do this in Python 3. Use nonlocal. total = [0] def f(m): total[0] += 1 return n + m def get_total(): return total[0] # see comments below about use of this pattern f.get_total = get_total return f def two_seven_example(): f27 = two_seven(2) def fx(): return f27(1) for _ in range(5): for name, func in (("calling func", fx), ("total times called", f27.get_total)): print("{}=".format(name, func())) two_seven_example() ``` -------------------------------- ### Closure Example: Adder Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Illustrates a closure used to create functions that add a specific number. The `adder` function returns a function `f` that remembers the `n` argument. ```python def adder(n): def f(m): return n + m return f add_two = adder(2) x = add_two(9) print(x) ``` -------------------------------- ### Enhanced GET Request Function with Headers Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Extends the `to_get` closure to include default headers. The inner function merges provided headers with default headers before making the request. ```python def to_get(base_url, headers=None): def f(segment, **kw): h = kw.get('headers', {}) if headers: headers.update(h) else: headers = h url = "/".join([base_url, segment]) kw['headers'] = headers print("Making request {} with headers:{}".format(url, headers)) return requests.get(url, **kw) return f ``` -------------------------------- ### Execute version check Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Executes the `run_example` utility with the '--version' argument to demonstrate the version checking functionality of the CLI. ```python run_example(runner, [('--version')]) ``` -------------------------------- ### CLI runner utility Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Provides a utility function `run_example` to test the command-line interface runner with different argument lists. It iterates through predefined arguments and calls the provided runner function. ```python ARGS = [["hello", 'my-name'], ["goodbye", 'my-new-name'], ["hello", '--help'], # ipython won't play nicely with the argparse useage of sys.exit ] def run_example(runner_func, args=ARGS): for arg in args: print("Running with args {}".format(arg)) runner_func(arg) ``` -------------------------------- ### Standard Function Definitions Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Illustrates defining functions using `def`, including a version with type annotations. Compares the results and prints function names and docstrings. ```python def f1(n): """A simple function to square a number""" return n * n # Or using type annotations in Python 3 def f2(n:int) -> int: return n * n assert f1(3) == f2(3) print(f1(3)) print((f1.__name__, f1.__doc__)) print((f1, f2)) ``` -------------------------------- ### Initializing Request Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Initializes a specific request function `rget` using the `to_get` factory, providing a base URL and default headers. ```python rget = to_get(BASE_URL, DEFAULT_HEADER) ``` -------------------------------- ### TodoClient Class Definition Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Defines the `TodoClient` class for interacting with a 'todos' service. It utilizes a `rget` function for making HTTP requests and includes methods for retrieving all todos and a specific todo by its ID. The `__init__` method sets up the client with a base URL and optional configurations. ```python class TodoClient(object): DEFAULT_HEADER = DEFAULT_HEADER def __init__(self, base_url, headers=None, logger=simple_logger, error_handler=default_error_handler): # making this public so one-off cases can leverage the rget model. This is dipping # down into the internals a bit. h = TodoClient.DEFAULT_HEADER if headers is None else headers self.rget = to_get(base_url, headers=h, error_handler=error_handler, logger=logger) def __repr__(self): return "".format(self.rget) def get_todos(self): return self.rget('todos', transform=to_todos) def get_todo_by_id(self, ix): return self.rget('todos/{}'.format(ix), transform=to_todo) ``` -------------------------------- ### Enhanced CLI with Optional Arguments (Python) Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Second iteration refactors the 'hello' command to accept an optional '--num-times' argument, demonstrating how to add new features and options to existing subparsers. It also shows how to pass arguments to the runner function for testing. ```python import argparse import sys def lib_hello(name, num_times=1): for _ in range(0, num_times): print('Hello, {0}!'.format(name)) def run_hello(args): return lib_hello(args.name, args.num_times) def _add_opt_name(p): p.add_argument('name', help="User Name") return p def add_opt_hello(p): _add_opt_name(p) p.add_argument('--num-times', help="Number of times to print hello", default=1, type=int) return p def get_parser(version='0.1.0'): parser = argparse.ArgumentParser(description="Help for Tool") parser.add_argument('--version', action='version', version=version) subparsers = parser.add_subparsers() def _add_to_sp(name, add_opts, func): p = subparsers.add_parser(name) add_opts(p) p.set_defaults(func=func) return p _add_to_sp('hello', add_opt_hello, run_hello) _add_to_sp('goodbye', _add_opt_name, run_goodbye) return parser def runner(argv): p = get_parser() pargs = p.parse_args(argv) return pargs.func(pargs) #if __name__ == '__main__': # sys.exit(runner(sys.argv[1:])) ``` -------------------------------- ### Python Decorator: Custom Timer Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-4.ipynb A Python decorator function `custom_timer` that wraps another function to measure and print the execution time. It utilizes `datetime` to record start and end times and formats a message with the function name, start time, duration, and arguments. ```python import time import datetime def custom_timer(f): def g(*args, **kw): started_at = datetime.datetime.now() result = f(*args, **kw) completed_at = datetime.datetime.now() dt = completed_at - started_at msg = "Ran func {} at {} in {:.2f} sec with args={} kw={}".format(f.__name__, started_at, dt.total_seconds(), args, kw) print(msg) return result return g @custom_timer def adder(a, b): time.sleep(0.25) return a + b def run_example(): nums = zip(range(1, 5), range(7, 11)) for a, b in nums: out = adder(a, b) ``` ```python run_example() ``` -------------------------------- ### Retrieving All Todos Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Shows how to call the `get_todos` method on the `TodoClient` instance to fetch a list of all todos. ```python todos = client.get_todos() ``` -------------------------------- ### Instantiating Modified TodoClient Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Creates an instance of the modified `TodoClient`. ```python client = TodoClient(BASE_URL) ``` -------------------------------- ### Basic CLI with Subparsers (Python) Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Initial implementation of a command-line interface using Python's `argparse` module. It defines basic commands like 'hello' and 'goodbye' with a shared 'name' argument and demonstrates how to structure CLI applications with subparsers. ```python import argparse import sys # Ideally, this should from my lib code and shouldn't # mix the IO layer from CLI interface def lib_hello(name): print('Hello, {0}!'.format(name)) def lib_goodbye(name): print('Goodbye, {0}!'.format(name)) # CLI. Wrapper funcs of (arg) -> f(a, b, c) def run_hello(args): return lib_hello(args.name) def run_goodbye(args): return lib_goodbye(args.name) def _add_opt_name(p): p.add_argument('name', help="User Name") return p def get_parser(version='0.1.0'): parser = argparse.ArgumentParser(description="Help for Tool") parser.add_argument('--version', action='version', version=version) subparsers = parser.add_subparsers() def _add_to_sp(name, add_opts, func): p = subparsers.add_parser(name) add_opts(p) p.set_defaults(func=func) return p _add_to_sp('hello', _add_opt_name, run_hello) _add_to_sp('goodbye', _add_opt_name, run_goodbye) return parser def runner(argv): p = get_parser() pargs = p.parse_args(argv) return pargs.func(pargs) #if __name__ == '__main__': # sys.exit(runner(sys.argv[1:])) ``` -------------------------------- ### Python filter function example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Shows a basic custom filter function that checks if a number is even and greater than or equal to 2. The `filter` function returns an iterable. ```python def custom_filter(x): return (x % 2) == 0 and x >= 2 fx = filter(custom_filter, range(0, 10)) print(fx) ``` -------------------------------- ### Composing multiple filters Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates applying multiple filters sequentially by piping the output of one `filter` call into another. It also shows how to define a helper function to compose filters. ```python import functools def custom_filter(min_value, x): return (x % 2) == 0 and x >= min_value def f1(x): return (x % 2) == 0 def example(): filter_min_4 = functools.partial(custom_filter, 4) s1 = filter(filter_min_4, range(1, 10)) s2 = filter(f1, s1) return s1 print(list(example())) ``` -------------------------------- ### Partial Application: Using functools.partial Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates how to use `functools.partial` to create a new function (`filter_lte_3`) by pre-filling the `min_value` argument of the `filter_min_value` function. ```python import functools filter_lte_3 = functools.partial(filter_min_value, 3) print(filter_lte_3) # Assuming XS is defined elsewhere, e.g., XS = [1, 2, 3, 4, 5] # print([x for x in XS if filter_lte_3(x)]) ``` -------------------------------- ### Import necessary libraries Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Imports core Python libraries required for building the command-line tool, including datetime, functools, itertools, argparse, sys, and logging. ```python import datetime import functools import itertools import argparse import sys import logging ``` -------------------------------- ### Configurable filter with partial application Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Creates a configurable filter function using a closure. It then demonstrates using `functools.partial` to apply a specific minimum value to the filter. ```python import functools def to_filter(min_value): def f(x): return (x % 2) == 0 and x >= min_value return f f = to_filter(2) XS = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(list(filter(f, range(-2, 10)))) ``` -------------------------------- ### Filter with partial application (alternative) Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Shows an alternative way to achieve a configurable filter using `functools.partial` with a function that accepts the minimum value as its first argument. ```python import functools def custom_filter(min_value, x): return (x % 2) == 0 and x >= min_value f = functools.partial(custom_filter, 2) XS = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(list(filter(f, XS))) ``` -------------------------------- ### Callable Class for Squaring Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates creating a callable object using a class that implements the `__call__` method for squaring. Also shows a class with initialization state and a bound method. ```python # Or using a class style. class F3(Callable): def __call__(self, n): return n * n f3 = F3() print(("Callable class {} {}".format(f3, f3(3)))) assert f3(3) == f1(3) # Perhaps a better example. class F4(Callable): def __init__(self, n): self.n = n def __repr__(self): _d = dict(k=self.__class__.__name__, n=self.n) return "<{k} n:{n}>".format(**_d) def __call__(self, n): return self.n * n def compute(self, n): return self.n * n f4 = F4(2) print(f4) assert f4(3) == 6 # You can also define a bound function f5 = f4.compute print(f5) assert f5(3) == 6 ``` -------------------------------- ### Import Necessary Libraries Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Imports essential libraries for making HTTP requests, functional programming utilities, data handling, date/time operations, logging, and data classes. ```python import requests import functools from collections import namedtuple import datetime import logging import sys # This requires python >= 3.7 from dataclasses import dataclass ``` -------------------------------- ### Caching Mechanism Example Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-4.ipynb Demonstrates a simple caching mechanism using a closure. The `compute` function returns a new function `f2` that caches results of the operation `f`. If a result for a given input pair is already in the cache, it's returned directly; otherwise, it's computed, stored, and then returned. ```python def compute(f=op.add): cache = {} def f2(m, n): key = (m, n) if key in cache: value = cache[key] print("Loading from cache {}".format(key, value)) return value else: v = f(m, n) cache[key] = v return v return f2 ``` -------------------------------- ### Map with context for reduction Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates using `map` to create tuples of (distance, point) and then using `functools.reduce` to find the point closest to the origin. ```python import math import functools def to_distance(x, y, x0=0, y0=0): def f(a, a0): return math.pow(a - a0, 2) return math.sqrt(f(x, x0) + f(y, y0)) def example(): points = ((5, 12), (6, 8), (3, 4)) f = lambda p: (to_distance(p[0], p[1]), p) mx = map(f, points) return functools.reduce(min, mx) print(example()) ``` -------------------------------- ### Generic Request Function with Transform Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Defines a `to_get` function factory that creates request functions. It allows specifying a base URL, default headers, and a transformation function to be applied to the response. It also includes basic logging of the request details. ```python import requests def null_tranform(response): return response def to_get(base_url, headers=None): def f(segment, **kw): url = "/".join([base_url, segment]) h = kw.get('headers', {}) headers.update(h) kw['headers'] = headers transform_func = kw.pop('transform', null_tranform) print("Making request {} with headers:{} with transform {}".format(url, headers, transform_func)) return transform_func(requests.get(url, **kw)) return f ``` -------------------------------- ### Argparse Parser Construction with Composition Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates building an argparse.ArgumentParser using function composition to add arguments. It defines helper functions for common argument groups and composes them to create parsers for different tools (alpha, beta, gamma), showcasing code reuse and modularity. ```python import argparse from functools import partial def compose(*funcs): def composed(arg): result = arg for f in funcs: result = f(result) return result return composed def _to_log_opt(p): p.add_argument('--level', help="Logging Level", default="INFO") return p def _to_opt_xy(p): p.add_argument('x', type=int, default=10, help="X Value") p.add_argument('y', type=int, default=20, help="Y Value") return p def _to_opt_z(p): p.add_argument('z', type=int, default=30, help="Z Value") return p def to_alpha_parser(name): p = argparse.ArgumentParser(description="Test Tool {}".format(name)) f = compose(_to_opt_z, _to_opt_xy, _to_log_opt) return f(p) # Example Usage: p = to_alpha_parser("Alpha") print(p.parse_args("1 2 3 --level=DEBUG".split())) ``` ```python import argparse from functools import partial def compose(*funcs): def composed(arg): result = arg for f in funcs: result = f(result) return result return composed def _to_opt(achar, default, help): def func(p): p.add_argument(achar, type=int, default=default, help=help) return p return func def _to_opt_xy(p): opts = [('--x', 10, "X Value"), ('--y', 20, "Y Value")] f_opt = lambda x: _to_opt(*x) opt_funcs = map(f_opt, opts) f = compose(*opt_funcs) return f(p) def to_parser(name, funcs): p = argparse.ArgumentParser(description="Test Tool {}".format(name)) f = compose(*funcs) return f(p) def to_alpha_parser(): funcs = (_to_opt_xy, _to_log_opt) return to_parser("Alpha", funcs) def to_beta_parser(): #let define this inline since it's not shared def f(p): p.add_argument('--min-radius', help="min radius", default=10) return p funcs = (f, _to_log_opt) return to_parser("Beta", funcs) def to_gamma_parser(): _to_opt_z = _to_opt('-z', 30, "Z Value") funcs = (_to_opt_z, _to_opt_xy, _to_log_opt) return to_parser("Gamma", funcs) # Example Usage: alpha_parser = to_alpha_parser() # Note, argparse is kinda brutal with it's use of sys.exit. # this doesn't really play well with the notebook # alpha_parser.parse_args(["--help"]) print(alpha_parser.parse_args("1 2 --level=WARN".split())) beta_parser = to_beta_parser() print(beta_parser.parse_args("--min-radius 11".split())) gamma_parser = to_gamma_parser() print(gamma_parser.parse_args("1 2 3 --level=ERROR".split())) ``` -------------------------------- ### Standard argparse execution pattern Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Illustrates the typical pattern for parsing command-line arguments and executing a function associated with a subparser in `argparse`. ```python p = get_parser() pargs = p.parse_args(sys.argv[1:]) return pargs.func(pargs) ``` -------------------------------- ### Fetch and Count All Todo Items Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Fetches all 'todo' items from the API, parses the JSON response, and prints the total count. ```python print("Found {} todos".format(len(rget("todos").json()))) ``` -------------------------------- ### Implementing Hooks with Null Functions (Python) Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Introduces the concept of hooks (pre-start and post-execution) by defining null/identity functions using Python's typing. This pattern enhances API extensibility by allowing users to plug in custom logic, similar to the Strategy pattern. ```python from typing import Optional import logging def setup_logger_null(level:str, output_file:Optional[str]=None) -> None: # implementation omitted for avoid chatter pass def runner(args, setup_logger=setup_logger_null) -> int: setup_logger(args.level, args.output_file) logger = logging.getLogger(__name__) ``` -------------------------------- ### Creating a Todo Object from Dictionary Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Demonstrates how to create a Todo object from a dictionary, mapping potentially different key names from the source data to the model's field names. ```python Todo.from_d(rget('todos/1').json()) ``` -------------------------------- ### Initializing Request Function with Error Handler Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Re-initializes the `rget` function, this time providing the `default_error_handler` to the `to_get` factory. ```python rget = to_get(BASE_URL, headers=DEFAULT_HEADER, error_handler=default_error_handler) ``` -------------------------------- ### Fetching a Non-existent Todo Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Attempts to retrieve a todo item with an ID that does not exist, demonstrating the error handling. ```python bad_todo = client.get_todo_by_id("DOES-NOT-EXIST") ``` -------------------------------- ### Retrieving a Specific Todo by ID Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Demonstrates fetching a single todo item using its unique identifier. ```python client.get_todo_by_id(2) ``` -------------------------------- ### Define Dataclass for Data Modeling Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Imports `dataclass` and `fields` from the `dataclasses` module, preparing for the definition of data models. ```python from dataclasses import dataclass, fields ``` -------------------------------- ### Top N Users Data Pipeline Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Constructs a data pipeline to filter users, map them to (age, user) tuples, find the top N users by age using a heap-based reducer, and then transform the results back into a list of User objects. ```python def to_pipeline(users_it, user_filter, max_users): reducer = compute_max_n(max_users) # Note, the map and filter are Iterables p1 = filter(user_filter, users_it) p2 = map(user_to_tuple, p1) # this is effectively the reduce step, # however, it results a list p3 = reducer(p2) # transform back to list[User] p4 = map(from_tuple, p3) return p4 ``` -------------------------------- ### User Data Structure and Generator Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Defines a `User` dataclass and a `to_users` generator function to create sample user data with random favorite colors. ```python XS = range(10) @dataclass(frozen=True) class User(object): user_id:int age:int first_name:str favorite_color:str def to_users(): colors = ['red', "blue", "black", "orange", "yellow"] def to_random(xs): return random.choice(xs) to_color = functools.partial(to_random, colors) names = [("Ralph", 13), ("Joe", 43),("Steve", 66), ("Rebecca", 41), ("Sam", 4), ("Richard", 32), ("Paul", 87), ("Stephen", 55), ("Sean", 2)] for i, (name, age) in enumerate(names): yield User(i + 1, age, name, to_color()) ``` -------------------------------- ### Importing Libraries Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-4.ipynb Imports the `datetime` module for date and time operations and the `operator` module for functional operations. ```python import datetime import operator as op ``` -------------------------------- ### Display current date Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb A simple Python snippet to print the current date and time. ```python print("Today is {}".format(datetime.datetime.now())) ``` -------------------------------- ### Converting map output to a list Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Shows how to consume the iterable returned by `map` by converting it into a list, applying the function to a range of numbers. ```python def f(x): return x * x print(list(map(f, range(1, 10)))) ``` -------------------------------- ### Current Date and Time Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb A simple Python snippet to print the current date and time. ```python print("Today is {}".format(datetime.datetime.now())) ``` -------------------------------- ### Executing Composed Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Executes the composed function `handle_error_to_todo` with a sample request. ```python handle_error_to_todo(rget('todos/1')) ``` -------------------------------- ### Fetching and Processing List of Todos Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Fetches a list of todos from the API using the `rget` function and the `to_todos` transformation, then prints the count and displays the last todo item. ```python todos = rget('todos', transform=to_todos) print("Found {} todos".format(len(todos))) todos[-1] ``` -------------------------------- ### Epilogue Execution Functions Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-3.ipynb Offers null and actual implementations for running epilogue tasks after a command has finished. The `run_epilogue` function calculates and prints the runtime. ```python def null_run_epilogue(exit_code:int, started_at) -> None: pass def run_epilogue(exit_code:int, started_at): dt = datetime.datetime.now() - started_at print("completed exit-code={} runtime:{:.2f} sec".format(exit_code, dt.total_seconds())) ``` -------------------------------- ### Define Base URL for API Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Sets the base URL for the JSONPlaceholder REST API, which will be used for making requests. ```python BASE_URL = "https://jsonplaceholder.typicode.com" ``` -------------------------------- ### Logger Functions for Request Pipeline Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Defines two logger functions: `null_logger` which does nothing, and `simple_logger` which logs the timestamp, URL, status code, and elapsed time of a response. These can be used with the `to_get` function. ```python import datetime def null_logger(response): return response def simple_logger(response): # the timestamp is a bit odd now = datetime.datetime.now() elapsed = response.elapsed.total_seconds() print("{} URL:{}\\n response:{} in {} sec".format(now.isoformat(), response.url, response.status_code, elapsed)) return response ``` -------------------------------- ### Fetch and Parse Single Todo Item Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-2.ipynb Uses the `rget` function to fetch a single 'todo' item from the API and parses the JSON response. ```python first_todo = rget("todos/1").json() ``` -------------------------------- ### Lambda Function for Squaring Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Defines and demonstrates a lambda function `f0` that squares its input. Shows how to access its name and print the function object. ```python f0 = lambda n: n * n f0(3) == 9 print(f0(3)) print(f0) print(f0.__name__) ``` -------------------------------- ### Current Date and Time Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Prints the current date and time using the `datetime` module and string formatting. ```python print("Today is {}".format(datetime.datetime.now())) ``` -------------------------------- ### Argparse Action for Printing Message and Exiting Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates creating a custom `argparse.Action` subclass within a factory function to print a message and exit the program upon encountering a specific command-line argument. ```python import argparse import sys def _to_print_message_action(msg): class PrintMessageAction(argparse.Action): """Print message and exit""" def __call__(self, parser, namespace, values, option_string=None): sys.stdout.write(msg + "\n") sys.exit(0) return PrintMessageAction ``` -------------------------------- ### Importing Libraries Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Imports necessary modules for functional programming, including datetime, collections, dataclasses, functools, itertools, and operator. Requires Python 3.7 or later. ```python import datetime import types import string import random from collections.abc import Iterable, Callable # Requires Python >= 3.7 from dataclasses import dataclass, fields import functools import itertools import operator as op ``` -------------------------------- ### Using Composed Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-1.ipynb Demonstrates how to use the implemented `compose` function to chain `incrementer`, `doubler`, and `squarer`. The resulting composed function `fx` is then applied to a list of numbers. ```python def squarer(a): return a * a def doubler(a): return 2 * a def incrementer(a): return a + 1 fx = compose(incrementer, doubler, squarer) print(list(map(fx, (1, 2, 7)))) ``` -------------------------------- ### Calling Cached Function Source: https://github.com/mpkocher/functional-programming-techniques-in-python/blob/main/Functional-Python-Part-4.ipynb Initializes the caching function and calls it with specific arguments. ```python f = compute() f(1, 2) ```