Partial methods in Python, enabled by the functools.partialmethod function, provides a way to create modified versions of existing functions. These modified versions, known as partial methods, are intentionally designed to serve as method definitions rather than being callable directly. They come in handy when customizing a function by fixing certain parameters.

functools.partialmethod in Python

In Python, functools.partialmethod is a function within the functools module. It returns a new partial method descriptor that behaves similarly to a partial function but is meant for method definitions. The resulting object is callable and can be treated as if it were the original function.

Want to customize class methods in Python without rewriting them? The functools.partialmethod function is your solution! Part of the functools module, it lets you create new methods for classes with pre-set arguments, similar to functools.partial but tailored for class methods. Let’s explore how partialmethod works, dive into practical examples, and discover how it can streamline your code!

What is functools.partialmethod?

The functools.partialmethod function is designed to create a new method for a class by pre-setting some arguments of an existing method. It’s like functools.partial, but specifically for methods, ensuring proper handling of the self parameter in class contexts. This makes it ideal for creating specialized versions of methods within a class, enhancing code reusability and clarity.

Imagine it as pre-configuring a class method with default settings, ready to be called with fewer arguments later.

Syntax: Clear and Concise

The syntax for functools.partialmethod is simple:

from functools import partialmethod

partialmethod(func, *args, **keywords)
  • func: The method (or function) to modify.
  • *args: Positional arguments to pre-set.
  • **keywords: Keyword arguments to pre-set.
  • Returns: A new method descriptor with pre-filled arguments, suitable for use in classes.

Why Use functools.partialmethod?

The partialmethod function is perfect for:

  • Creating specialized versions of class methods without duplicating code.
  • Simplifying method calls by pre-setting common arguments.
  • Improving readability by reducing repetitive argument passing in class methods.
  • Supporting functional programming patterns within object-oriented code.

Let’s See partialmethod in Action

Here are practical examples to show how functools.partialmethod can make your Python classes more efficient and elegant.

Example 1: Customizing a Logging Method

Create specialized logging methods for a class with pre-set log levels.

from functools import partialmethod

class Logger:
    def log(self, level, message):
        return f"[{level}] {message}"

    error = partialmethod(log, "ERROR")
    info = partialmethod(log, "INFO")

logger = Logger()
print(logger.error("File not found"))
print(logger.info("Application started"))

Output:

[ERROR] File not found
[INFO] Application started

Example 2: Pre-setting Method Parameters

Simplify a class method that formats numbers by pre-setting the precision.

from functools import partialmethod

class NumberFormatter:
    def format_number(self, number, precision):
        return f"{number:.{precision}f}"

    two_decimal = partialmethod(format_number, precision=2)
    three_decimal = partialmethod(format_number, precision=3)

formatter = NumberFormatter()
print(formatter.two_decimal(3.14159))
print(formatter.three_decimal(3.14159))

Output:

3.14
3.142

Example 3: Customizing Notifications

Create specialized notification methods with a fixed channel (e.g., email or SMS).

from functools import partialmethod

class Notifier:
    def send(self, channel, message):
        return f"Sending via {channel}: {message}"

    email = partialmethod(send, "Email")
    sms = partialmethod(send, "SMS")

notifier = Notifier()
print(notifier.email("Meeting at 10 AM"))
print(notifier.sms("Your code is 1234"))

Output:

Sending via Email: Meeting at 10 AM
Sending via SMS: Your code is 1234

Example 4: Using Keyword Arguments

Pre-set keyword arguments for a method that calculates discounts with a fixed currency.

from functools import partialmethod

class Shop:
    def calculate_discount(self, price, percentage, currency="USD"):
        discount = price * (percentage / 100)
        return f"Discount: {discount:.2f} {currency}"

    ten_percent_usd = partialmethod(calculate_discount, percentage=10, currency="USD")
    twenty_percent_eur = partialmethod(calculate_discount, percentage=20, currency="EUR")

shop = Shop()
print(shop.ten_percent_usd(100))
print(shop.twenty_percent_eur(100))

Output:

Discount: 10.00 USD
Discount: 20.00 EUR

Example 5: Combining with Instance Attributes

Use partialmethod to create methods that leverage instance attributes, like a greeting with a fixed prefix.

from functools import partialmethod

class Greeter:
    def __init__(self, prefix):
        self.prefix = prefix

    def greet(self, name, punctuation):
        return f"{self.prefix}, {name}{punctuation}"

    hello_exclaim = partialmethod(greet, punctuation="!")
    hello_question = partialmethod(greet, punctuation="?")

greeter = Greeter("Hello")
print(greeter.hello_exclaim("Alice"))
print(greeter.hello_question("Bob"))

Output:

Hello, Alice!
Hello, Bob?

Example 6: Reusing Methods Across Instances

Create a method to scale values with a fixed factor, reusable across different instances.

from functools import partialmethod

class Scaler:
    def scale(self, value, factor):
        return value * factor

    double = partialmethod(scale, factor=2)
    triple = partialmethod(scale, factor=3)

scaler = Scaler()
print(scaler.double(5))
print(scaler.triple(5))

Output:

10
15

Key Takeaways

Here’s what makes functools.partialmethod a powerful tool:

  • Creates specialized class methods by pre-setting arguments, reducing code duplication.
  • Handles the self parameter automatically, unlike functools.partial.
  • Supports both positional and keyword arguments for flexibility.
  • Enhances readability by simplifying method calls in classes.
  • Perfect for creating reusable, customized methods in object-oriented programming.

Pro Tip

Use partialmethod when you want to define multiple variations of a method in a class without cluttering it with redundant code. It’s especially useful for creating intuitive interfaces, like specialized logging or notification methods, that feel natural to use.

from functools import partialmethod

class TaskManager:
    def report(self, status, task):
        return f"Task '{task}' is {status}"

    completed = partialmethod(report, "Completed")
    failed = partialmethod(report, "Failed")

manager = TaskManager()
print(manager.completed("Backup"))
print(manager.failed("Sync"))

Output:

Task 'Backup' is Completed
Task 'Sync' is Failed

Wrapping Up

The functools.partialmethod function is a fantastic tool for customizing class methods in Python. By pre-setting arguments, it simplifies method calls, reduces code repetition, and blends functional programming with object-oriented design. Whether you’re building specialized logging, formatting, or notification methods, partialmethod makes your classes cleaner and more intuitive. Try it in your next Python project to elevate your code’s elegance and efficiency!

For example, if a function requires two parameters, we can create a partial function from it that takes the first parameter as an argument. Later, we can call it using the other value as the parameter, making it easier to create modified versions of existing functions.

Syntax

The signature for the partialmethod function is as follows. This function returns a new partial method descriptor that, when called, behaves like a normal function, with ‘self’ as the first positional argument, ‘args’ as in a partial function, and keyword arguments as keywords. If additional arguments are passed to the call, they will be appended to ‘args’. Similarly, if additional keyword arguments are provided, they will extend and override the existing keywords.

partial(func,/,*args,*keywords)

Python partialmethod Examples:

Example 1:

In this scenario, we set the city to “Hyderabad” during object creation. Later on, we have the flexibility to change this value by assigning any desired city name of our choice.

from functools import partialmethod

class PartialDemo:
    def __init__(self):
        self.city = 'Hyderabad'
    # Defining method
    def _city(self, type):
        self.city = type
  
  #Using partialmethod
    set_mumbai = partialmethod(_city, type ='mumbai')
    set_chennai = partialmethod(_city, type ='chennai')
      
#Object creation  
obj = PartialDemo()
print(obj.city)

#Resetting city value
obj.set_chennai()
print(obj.city)

Output:

Hyderabad
chennai

Example 2:

In this instance, we are calculating the simple interest for both a savings account and a current account, each with its own distinct rate of interest.

from functools import partialmethod

class PartialDemo:
    def __init__(self):
        self.interest = 0
        self.principal=1000
    # Defining method
    def _si(self, r,n):
        self.interest=self.principal*r*n/100.0
  
  #Using partialmethod
    set_current = partialmethod(_si, r=3,n=3)
    set_saving = partialmethod(_si, r=7,n=3)
  
#Object creation  
obj = PartialDemo()
print(obj.interest)

#computing for current account 
obj.set_current()
print("Interest on current account for principal of 1000 will be",obj.interest)

#computing for saving account
obj.set_saving()
print("Interest on current account for principal of 1000 will be",obj.interest)

Output:

0
Interest on current account for principal of 1000 will be 90.0
Interest on current account for principal of 1000 will be 210.0

Example 3:

In this scenario, we are calculating the summation of numbers in the series up to the given number ‘n’.

from functools import partialmethod
 
class PartialDemo:
    def __init__(self):
        self.sum=0
    # Defining method
    def _series(self, n):
        for i in range(1,n+1):
            self.sum=self.sum+i
  
  #Using partialmethod
    set_series=partialmethod(_series,5)
 
#Object creation  
obj = PartialDemo()
print(obj.sum)

#computing for current account 
obj.set_series()
print(obj.sum)

Output:

0
15

Conclusion:

Therefore, this method provides a descriptor that is not intended to be directly callable. Instead, it serves as a means to define new methods.

References

Happy Learning 🙂