Hrvoje T
Hrvoje T

Reputation: 3923

How to combine functions in a class in Python?

I have a code like this. In my original code I would like to call two or more methods when a button is clicked.

class MyApp(object):

    @staticmethod
    def combine_funcs(*funcs):
        def combined_func(*args, **kwargs):
            for f in funcs:
                f(*args, **kwargs)
        return combined_func

    def __init__(self):
        MyApp.combine_funcs(self.my_first_func, self.my_second_func)

    def my_first_func(self):
        print("First")

    def my_second_func(self):
        print("Second")


my_app = MyApp()

When I run this nothing is printed. Why?

Upvotes: 0

Views: 4128

Answers (2)

Serge Ballesta
Serge Ballesta

Reputation: 149085

A method is not much more than a member of a class that happens to be a function. The magic is just that when it is called on an object (a.func(x...)) the object is automatically prepended in the argument list resulting in the actual call A.func(a, x...).

What you want can be obtained that way:

class MyApp(object):
    # prepend with _ to "hide" the symbol
    def _combine_funcs(*funcs):      
        def combined_func(*args, **kwargs):
            for f in funcs:
                f(*args, **kwargs)
        return combined_func

    def my_first_func(self):
        print("First")

    def my_second_func(self):
        print("Second")

    # actually declares combined as a method of MyClass
    combined = _combine_funcs(my_first_func, my_second_func)

Usage:

>>> a = MyApp()
>>> a.combined()
First
Second

But a much cleaner way would be to use a decorator for that in Python 3:

import inspect

def combine_funcs(*funcs):
    def outer(f):
        def combined_func(*args, **kwargs):    # combine a bunch of functions
            for f in funcs:
                x = f(*args, *kwargs)
            return x                           # returns return value of last one
        combined_func.__doc__ = f.__doc__      # copy docstring and signature
        combined_func.__signature__ = inspect.signature(f)
        return combined_func
    return outer

class MyApp(object):

    def my_first_func(self):
        print("First")

    def my_second_func(self):
        print("Second")

    @combine_funcs(my_first_func, my_second_func)
    def combined(self):
        """Combined function"""
        pass

The decorator will replace the body but will keep the docstring and the signature of the original function (in Python 3). You can then use it normally:

>>> a = MyApp()
>>> a.combined()
First
Second
>>> help(a.combined)
Help on method combined_func in module __main__:

combined_func() method of __main__.MyApp instance
    Combined function

>>> help(MyApp.combined)
Help on function combined_func in module __main__:

combined_func(self)
    Combined function

Upvotes: 2

16shells
16shells

Reputation: 106

You're calling combine_funcs, but the function within that, combined_func, is not being called.

Is there a reason not to simplify to:

class MyApp(object):

    @staticmethod
    def combined_func(*args, **kwargs):
        for f in args:
            f()

    def __init__(self):
        MyApp.combined_func(self.my_first_func, self.my_second_func)

    def my_first_func(self):
        print("First")

    def my_second_func(self):
        print("Second")

my_app = MyApp()

Upvotes: 0

Related Questions