Викличте метод Python за іменем


82

Якщо у мене є рядок об’єкта та імені методу, як я можу викликати метод?

class Foo:
    def bar1(self):
        print 1
    def bar2(self):
        print 2

def callMethod(o, name):
    ???

f = Foo()
callMethod(f, "bar1")

1
Хоча вони схожі, це не є точною копією цього питання , яке запитує про функцію в модулі, а не про метод об'єкта.
Grumdrig

Дуже тісно пов'язані (чорт візьми, можливо, навіть дурень): Як отримати доступ до атрибута об'єкта, заданого рядком, що відповідає імені цього атрибута . Методи - це також атрибути.
Аран-Фей

Відповіді:


113

Використовуйте вбудовану getattr()функцію:

class Foo:
    def bar1(self):
        print(1)
    def bar2(self):
        print(2)

def call_method(o, name):
    return getattr(o, name)()


f = Foo()
call_method(f, "bar1")  # prints 1

Ви також можете використовувати setattr()для встановлення атрибутів класу за іменами.


Я не міг знайти, що шукати в документації! Дякую!
Джаз

@Jazz, це вбудовано. Можливо, вам доведеться здійснити пошук на сторінці зC-f
aaronasterling

@aaronasterling Я знаю, але не зміг знайти, за яким словом шукати!
Джаз

чому callMethod (f, "bar1") не називається f.callMethod (f, "bar1")
Філіп Путенвіла

PhilipJ, тому що це просто метод, визначений парою рядків раніше, поза класом Foo:.
Енріко Карлессо

6

У мене було подібне запитання, я хотів викликати метод екземпляра за посиланням. Ось кумедні речі, які я знайшов:

instance_of_foo=Foo()

method_ref=getattr(Foo, 'bar')
method_ref(instance_of_foo) # instance_of_foo becomes self

instance_method_ref=getattr(instance_of_foo, 'bar')
instance_method_ref() # instance_of_foo already bound into reference

Python дивовижний!


2
getattr(globals()['Foo'](), 'bar1')()
getattr(globals()['Foo'](), 'bar2')()

Не потрібно спочатку створювати екземпляр Foo!


Це був лише приклад, у мене є справжній екземпляр реального класу!
Джаз,

2
Виклик методу неініціалізованого класу може означати, що ви робите щось не так.
Енріко Карлессо,

що якщо fooнемає в глобальних мережах?
aaronasterling

1
це, мабуть, ні, але ви повинні подбати про те, що Fooє. ;)
johndodo


0

Ось більш узагальнена версія з використанням декораторів Python. Ви можете телефонувати за коротким або довгим ім'ям. Я знайшов це корисним при реалізації CLI з короткими та довгими підкомандами.

Пітонові декоратори чудові. Брюс Еккел («Мислення на Java») чудово описує тут декоратори Python.

http://www.artima.com/weblogs/viewpost.jsp?thread=240808 http://www.artima.com/weblogs/viewpost.jsp?thread=240845

#!/usr/bin/env python2

from functools import wraps


class CommandInfo(object):
    cmds = []

    def __init__(self, shortname, longname, func):
        self.shortname = shortname
        self.longname = longname
        self.func = func


class CommandDispatch(object):
    def __init__(self, shortname, longname):
        self.shortname = shortname
        self.longname = longname

    def __call__(self, func):
        print("hello from CommandDispatch's __call__")

        @wraps(func)
        def wrapped_func(wself, *args, **kwargs):
            print('hello from wrapped_func, args:{0}, kwargs: {1}'.format(args, kwargs))
            func(wself, *args, **kwargs)

        ci = CommandInfo
        ci.cmds += [ci(shortname=self.shortname, longname=self.longname, func=func)]
        return wrapped_func

    @staticmethod
    def func(name):
        print('hello from CommandDispatch.func')

        for ci in CommandInfo.cmds:
            if ci.shortname == name or ci.longname == name:
                return ci.func

        raise RuntimeError('unknown command')


@CommandDispatch(shortname='co', longname='commit')
def commit(msg):
    print('commit msg: {}'.format(msg))


commit('sample commit msg')         # Normal call by function name

cd = CommandDispatch
short_f = cd.func(name='co')        # Call by shortname
short_f('short sample commit msg')

long_f = cd.func(name='commit')     # Call by longname
long_f('long sample commit msg')


class A(object):
    @CommandDispatch(shortname='Aa', longname='classAmethoda')
    def a(self, msg):
        print('A.a called, msg: {}'.format(msg))


a = A()
short_fA = cd.func(name='Aa')
short_fA(a, 'short A.a msg')

long_fA = cd.func(name='classAmethoda')
long_fA(a, 'short A.a msg')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.