python: передача метода экземпляра и его аргумента в функцию для выполнения

Я знаю, как передать функцию в качестве аргумента, а затем использовать функцию позже. Однако я не мог понять, как это сделать с помощью метода. Я хотел бы предоставить пользователям API, который позволяет им настраивать пользовательские функции.

В качестве (не) рабочего примера, скажем, я хочу, чтобы пользователь мог предоставить некоторую общую функцию pandas:

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)
    

приведенный выше код приводит к

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

по понятным причинам. Как я могу добиться желаемого поведения, которое позволяет пользователю предоставлять различные функции?


person Dr-Nuke    schedule 04.11.2020    source источник
comment
Возможно, сохраните только имя метода в виде строки, то есть myfun = 'merge', а затем result = getattr(left, exec_dict['func'])(**exec_dict['args']).   -  person mportes    schedule 04.11.2020
comment
Или вызовите exec_dict['func'] напрямую и укажите left в качестве первого аргумента, то есть exec_dict['func'](left, **exec_dict['args']).   -  person mportes    schedule 04.11.2020
comment
Вы можете создать минимально воспроизводимый пример. Вызов метода — это то, что вы можете просто сделать; затемнение вашей цели с помощью pandas и нескольких словарей излишне усложняет ситуацию как для нас, и для вас.   -  person MisterMiyagi    schedule 04.11.2020


Ответы (2)


Поэтому я немного упростил вашу проблему и опустил pandas. Вызвать метод экземпляра по имени можно с помощью метода getattribute:

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'}
)
person AlexNe    schedule 04.11.2020
comment
это идеальное, общее, высокоуровневое решение моей проблемы. Спасибо! - person Dr-Nuke; 04.11.2020

Методы — это, грубо говоря, функции, которые получают экземпляр в качестве первого аргумента. exec_dict['func'] уже является методом, его не нужно искать на left. Просто передайте left в качестве первого аргумента.

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

Этот код работает для любого вызываемого объекта, который принимает фрейм данных left в качестве первого аргумента, а не только для методов фрейма данных.


Вместо того, чтобы ожидать func и args в словаре, exec_func может получить их напрямую. left_on также можно передать напрямую.

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)

Обратите внимание, что обычно используется args для positional и kwargs для аргументов/параметров keyword, т.е. def exec_func(func, *args, **kwargs): ....

person MisterMiyagi    schedule 04.11.2020
comment
Я благодарю ваш ответ за то, что он точно указал на мой вопрос. Мне точно помогает, с минимальными изменениями. Спасибо! Я соглашусь с ответом выше, так как это более высокий уровень, общий ответ, но я также хотел бы принять ваш! - person Dr-Nuke; 04.11.2020
comment
@Dr-Nuke Не беспокойтесь. Было приятно увидеть и ответить на правильный вопрос! - person MisterMiyagi; 04.11.2020