Викличте метод 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.