Adventures in Machine Learning

Boosting Functionality in Python with Functools Module: Understanding partialmethod()

Functional Patterns in Python using functools module

Python is a dynamic programming language that supports higher-order functions, also known as functions that work with other functions. These higher-order functions allow developers to write cleaner and more efficient code.

Python’s functools module provides a set of functions that implement functional programming patterns that can be used to extend the functionality of existing functions, improve code readability, and write efficient code. In this article, we will explore some of the commonly used functools functions and their usage.

List of functools functions covered

This article covers the following functools functions that are commonly used in functional programming patterns in Python:

– partial

– partialmethod

– reduce

– wraps

– lru_cache

– cache

– cached_property

– total_ordering

– singledispatch

Explanation and Usage

partial()

The partial() function is one of the most commonly used functions in the functools module. This function allows the developer to create a new function by fixing a set of arguments to an existing function.

The partial() function is defined as follows:

“`python

def partial(func, *args, **keywords):

def newfunc(*fargs, **fkeywords):

return func(*(args + fargs), **dict(keywords, **fkeywords))

newfunc.func = func

newfunc.args = args

newfunc.keywords = keywords

return newfunc

“`

The partial() function takes an existing function and fixes a set of input arguments to it. The returned function can then be called with the remaining arguments, which are passed to the original function.

This allows the developer to create a new function that is a simplified version of the original, taking fewer arguments.

Example

Let’s look at an example of using the partial() function in Python:

“`python

from functools import partial

def square(number, power=2):

return number ** power

square_of_numbers = partial(square, power=2)

print(square(2)) # Output: 4

print(square_of_numbers(2)) # Output: 4

“`

In the example above, we define a function called square() that takes two parameters, number and power, and returns the square of the number raised to the power. We then use the partial() function to create a new function called square_of_numbers, which fixes the power argument to 2.

This creates a new function that is a simpler version of the original, taking only one argument, number.

partialmethod()

The

partialmethod() function is similar to the partial() function, but it is used to create partial objects for methods of a class. The

partialmethod() function is defined as follows:

“`python

def partialmethod(func, *args, **keywords):

def newfunc(self, *fargs, **fkeywords):

return func(self, *(args + fargs), **dict(keywords, **fkeywords))

newfunc.func = func

newfunc.args = args

newfunc.keywords = keywords

return newfunc

“`

Example

Let’s look at an example of using the

partialmethod() function in Python:

“`python

from functools import partialmethod

class Character:

def __init__(self, name):

self._name = name

def attack(self, power, target):

print(f'{self._name} attacks {target} with power of {power}.’)

orc = Character(‘Orc’)

orc.attack(10, ‘Elf’)

orc_slash = partialmethod(Character.attack, power=5)

orc_slash(orc, ‘Elf’)

“`

In the example above, we define a class called Character and a method called attack(), which takes two arguments, power and target, and prints a message. We then use the

partialmethod() function to create a new method called orc_slash, which fixes the value of the power argument to 5.

This gives us a new method that is a simplified version of the original, taking only one argument, the target.

reduce()

The

reduce() function is used to apply a function to a list of elements, reducing it to a single accumulated value. The

reduce() function is defined as follows:

“`python

def reduce(function, iterable, initializer=None):

it = iter(iterable)

if initializer is None:

value = next(it)

else:

value = initializer

for element in it:

value = function(value, element)

return value

“`

Example

Let’s look at an example of using the

reduce() function in Python:

“`python

from functools import reduce

numbers = [1, 2, 3, 4, 5]

sum_of_numbers = reduce(lambda x, y: x + y, numbers)

print(sum_of_numbers) # Output: 15

“`

In the example above, we define a list called numbers and use the

reduce() function to apply a lambda function to it, which adds the elements of the list together. The result is a single accumulated value, the sum of the numbers in the list.

wraps()

The

wraps() function is a decorator function that is used to update the metadata of an existing function to preserve its information. The

wraps() function is defined as follows:

“`python

import functools

def wraps(wrapped,

assigned=functools.WRAPPER_ASSIGNMENTS,

updated=functools.WRAPPER_UPDATES):

return functools.partial(functools.update_wrapper, wrapped=wrapped,

assigned=assigned, updated=updated)

“`

Example

Let’s look at an example of using the

wraps() function in Python:

“`python

from functools import wraps

def my_decorator(func):

@wraps(func)

def wrapper(*args, **kwargs):

“””Wrapper function”””

print(“Starting”)

result = func(*args, **kwargs)

print(“Finished”)

return result

return wrapper

@my_decorator

def my_function():

“””Function”””

print(“Hello, world”)

print(my_function.__name__) # Output: “my_function”

print(my_function.__doc__) # Output: “Function”

“`

In the example above, we define a decorator function called my_decorator that prints a message before and after executing an existing function. We then use the

wraps() function to update the metadata of the wrapper function, preserving the original function’s name and docstring.

lru_

cache()

The

lru_

cache() function is a decorator function used to cache results of the callable function based on the arguments. This function makes use of the Least Recently Used (LRU) cache algorithm to store frequent results and discard the rarely used cache as the cache size exceeds the maxsize.

The

lru_

cache() function is defined as follows:

“`python

def lru_cache(maxsize=128, typed=False):

def decorator(func):

… return decorator

“`

Example

Let’s look at an example of using the

lru_

cache() function in Python:

“`python

from functools import lru_cache

@lru_cache(maxsize=128)

def fibonacci(number):

“””Return the nth number in the Fibonacci series.”””

if number == 0:

return 0

elif number == 1:

return 1

else:

return fibonacci(number – 1) + fibonacci(number – 2)

print([fibonacci(n) for n in range(16)]) # Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

“`

In the example above, we define a function called fibonacci() that recursively calculates the nth number in the Fibonacci series. We then use the

lru_

cache() function to cache previously calculated results, which improves the performance of the function and reduces the number of recursive calls.

cache()

The

cache() function is used to cache the results of a callable function based on the arguments. The

cache() function is defined as follows:

“`python

def cache(user_function):

“””

Simple cache function that saves the result of the callable for future look ups.

“””

cache_map = {}

def wrapped_function(*args, **kwargs):

kernel = (args, tuple(kwargs.items()))

if kernel not in cache_map:

cache_map[kernel] = user_function(*args, **kwargs)

return cache_map[kernel]

return wrapped_function

“`

Example

Let’s look at an example of using the

cache() function in Python:

“`python

from functools import cache

@cache

def factorial(number):

“””Return the factorial of a number.”””

if number == 1:

return 1

else:

return number * factorial(number – 1)

print(factorial(5)) # Output: 120

“`

In the example above, we define a function called factorial() that recursively calculates the factorial of a number. We then use the

cache() function to cache previously calculated results, which improves the performance of the function and reduces the number of recursive calls.

cached_property()

The

cached_property() function is a decorator function that is used to create managed attributes with caching functionality. The

cached_property() function is defined as follows:

“`python

class cached_property:

def __init__(self, func):

self.func = func

def __get__(self, instance, owner=None):

if instance is None:

return self

value = self.func(instance)

setattr(instance, self.func.__name__, value)

return value

“`

Example

Let’s look at an example of using the

cached_property() function in Python:

“`python

from functools import cached_property

class Circle:

def __init__(self, radius):

self.radius = radius

@cached_property

def area(self):

“””Return the area of the circle.”””

return 3.14 * self.radius ** 2

circle = Circle(5)

print(circle.area) # Output: 78.5

circle.radius = 10 # the cached value will still be 3.14 * 5 * 5, since it’s already calculated

print(circle.area) # Output: 78.5

“`

In the example above, we define a class called Circle that has a property called area which returns the area of the circle. We use the

cached_property() function to cache the property value so that it is calculated once and stored for future lookups.

total_ordering()

The

total_ordering() function is a class decorator that is used to define overloaded implementations of comparison operators. The

total_ordering() function is defined as follows:

“`python

class total_ordering:

def __init__(self, cls):

self.cls = cls

def __set_name__(self, owner, name):

if not hasattr(self.cls, “__lt__”):

raise ValueError(f”Cannot create total_ordering after {name} was already defined.”)

def __call__(self, cls, **kwargs):

self.cls = cls

self.__lt__ = kwargs.pop(“__lt__”, self.__lt__)

self.__le__ = kwargs.pop(“__le__”, self.__le__)

self.__eq__ = kwargs.pop(“__eq__”, self.__eq__)

self.__ne__ = kwargs.pop(“__ne__”, self.__ne__)

self.__ge__ = kwargs.pop(“__ge__”, self.__gt__)

self.__gt__ = kwargs.pop(“__gt__”, self.__gt__)

return self

def __lt__(self, other):

raise TypeError(“Cannot compare two objects”)

def __le__(self, other):

return self == other or self < other

def __eq__(self, other):

raise TypeError(“Cannot compare two objects”)

def __ne__(self, other):

return not self == other

def __ge__(self, other):

return self == other or self > other

def __gt__(self, other):

raise TypeError(“Cannot compare two objects”)

“`

Example

Let’s look at an example of using the

total_ordering() function in Python:

“`python

from functools import total_ordering

@total_ordering

class Player:

def __init__(self, name, score):

self.name = name

self.score = score

def __lt__(self, other):

return self.score < other.score

john = Player(‘John’, 45)

mary = Player(‘Mary’, 99)

print(john <= mary) # Output: True

“`

In the example above, we define a class called Player that has two properties, name and score. We use the

total_ordering() function to define overloaded implementations of comparison operators so that we can compare instances of the Player class using the less than, greater than, less than or equal to, and greater than or equal to operators.

singledispatch()

The

singledispatch() function is a decorator function that is used to define a generic implementation of a function as well as the specific implementations of that function for different argument types. The

singledispatch() function is defined as follows:

“`python

def singledispatch(function):

registry = {}

def dispatch(type_):

return registry.get(type_, function)

def decorator(func):

registry[object] = function

registry[func.__annotations__.get(‘type’, object)] = func

func.dispatch = dispatch

func.registry = registry

return func

dispatch.register = decorator

dispatch.registry = registry

return dispatch

“`

Example

Let’s look at an example of using the

singledispatch() function in Python:

“`python

from functools import singledispatch

@singledispatch

def greet(name):

print(f’Hello, {name}!’)

@greet.register(int)

def _(age):

print(f’Hello, age is {age}!’)

greet(‘Alice’) # Output: “Hello, Alice!”

greet(25) # Output: “Hello, age is 25!”

“`

In the example above, we define a function called greet that has a generic implementation for greeting a person. We use the

singledispatch() function to define specific implementations for greeting a person based on their type, like int.

We then call the function twice, once with a string argument and once with an integer argument, and it prints out the respective messages.

Conclusion

The functools module provides a set of functions that allow developers to write cleaner and more efficient code. These higher-order functions can be used to extend the functionality of existing functions, improve code readability, and write efficient code.

By using functions like partial(),

reduce(),

cached_property(), and

total_ordering(), developers can write code that is more modular, reusable, and maintainable. With the help of these functions, Python becomes even more powerful and versatile, providing developers with the tools they need to write code that is easy to understand and maintainable.

3)

partialmethod() function in functools module

In Python classes, a method is a function that is defined inside a class and is intended to operate on an instance of that class. The method is often used to perform some kind of behavior on the object.

Python’s functools module provides the

partialmethod() function which is used to apply partial arguments to class methods, making it easier to set default values for arguments in methods. This article will focus on the

partialmethod() function and how it can be used to create convenient methods that set values for class instances.The

partialmethod() function in the functools module is a variant of the partial() function, and is used to apply partial arguments to class methods.

Instead of creating a new function object, a partial method object is created that can be used to set default values for the arguments of the method.

Example

Let’s look at an example of using the

partialmethod() function to create convenience methods in Python:

“`python

from functools import partialmethod

class Character:

def __init__(self, name, has_magic=False, magic=None):

self.name = name

self.has_magic = has_magic

self.magic = magic

def set_has_magic(self, has_magic: bool):

self.has_magic = has_magic

def set_magic(self, magic: str):

self.magic = magic

set_has_magic_on = partialmethod(set_has_magic, True)

set_has_magic_off = partialmethod(set_has_magic, False)

set_magic_fire = partialmethod(set_magic, “fire”)

set_magic_ice = partialmethod(set_magic, “ice”)

“`

In the example above, we have defined a class called `Character` with an `__init__` method that takes in arguments such as name, has_magic, and magic. We also have defined two methods called `set_has_magic` and `set_magic` that allow users to set values for these attributes.

We use the `partialmethod` function to create four new methods that set default values for these attributes:

– `set_has_magic_on` sets the value