2

I know how to pass a function as argument, and then use the function later. However I could not figure out how to do this with a method. I'd like to provide users with an "API" that allows them to configure custom functions.

As a (not) working example, let's say I want the user to be able to supply some general pandas function:

import pandas as pd

# user-supplied data
right = pd.DataFrame({'right_a':[1,2],
                      'right_b':[3,4]})

# user-supplied function & arguments 
myfun = pd.DataFrame.merge
args = {'right':right,
        'right_on':'right_a',
        'how':'inner'
        } 

user_configs = {'func': myfun,
                'args':args}

def exec_func(exec_dict):
    # some data that only the function knows:
    left = pd.DataFrame({'left_a':[1,2],'left_b':['foo','bar']})
    
    # some args that only the function knows
    exec_dict['args']['left_on'] = 'left_a'
    
    # apply the function
    #result = left.merge(**exec_dict['args'])  # desired result
    result = left.exec_dict['func'](**exec_dict['args']) # generalized but not working code
    return result

exec_func(user_configs)
    

the above code result in a

AttributeError: 'DataFrame' object has no attribute 'exec_dict'

for obvious reasons. How could i achieve the desired behavior, that allows the user to supply different functions?

3
  • Maybe store just the method name as a string, i.e. myfun = 'merge' and then result = getattr(left, exec_dict['func'])(**exec_dict['args']). Commented Nov 4, 2020 at 14:34
  • 1
    Or, call exec_dict['func'] directly and provide left as the first argument, i.e. exec_dict['func'](left, **exec_dict['args']). Commented Nov 4, 2020 at 14:39
  • You might want to construct a minimal reproducible example. Calling a method is something you can literally just do; obscuring your goal with pandas and several dictionaries is making things needlessly complicated both for us and you. Commented Nov 4, 2020 at 14:46

2 Answers 2

1

So I have simplified your problem a little and omitted pandas. The way to go about calling a method of an instance by name is via the getattribute method:

class MethodProvider (object):
    
    def __init__(self):
        pass
    
    def method1(self, foo):
        print('Method1 was called with arguments %s'%foo)
    
    def method2(self, baz):
        print('Method2 was called with argument %s'%baz)
        
def exec_method(obj, meth, kwargs):
    func = obj.__getattribute__(meth)
    func(**kwargs)

# Test construction 
mp = MethodProvider()

exec_method(
    mp,
    'method1',
    {'foo': 'bar'}
)

exec_method(
    mp,
    'method2',
    {'baz': 'buzz'}
)
Sign up to request clarification or add additional context in comments.

1 Comment

this is a perfect, general, high lvl solution to my problem. Thanks!
1

Methods are, loosely speaking, functions that receive the instance as the first argument. exec_dict['func'] already is the method, it does not need to be looked up on left. Simply pass left as the first argument.

   #        |method/function|| called with...          |
   result = exec_dict['func'](left, **exec_dict['args'])
   #                         | instance
   #                                | further arguments

This code works for any callable that takes the dataframe left as its first argument, not just dataframe methods.


Instead of expecting func and args wrapped in a dictionary, exec_func can directly receive them. left_on can also be passed directly.

def exec_func(func, **args):
    left = pd.DataFrame({'left_a':[1,2],'left_b':['foo','bar']})
    # apply the function
    result = func(left, left_on='left_a', **args)
    return result

exec_func(**user_configs)

Note that it is customary to use args for positional and kwargs for keyword arguments/parameters, i.e. def exec_func(func, *args, **kwargs): ....

2 Comments

I commend your answer for being pinpointed to my question. It exactly helps me, with minimal changes. Thanks! I'll give the accept to the answer above, as is is the more high-lvl, general answer, but i'd like to accept yours as well!
@Dr-Nuke No worries. It was a pleasure to see and answer a proper question!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.