Wywołaj metodę Pythona według nazwy

82

Jeśli mam obiekt i nazwę metody w ciągu, jak mogę wywołać metodę?

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

def callMethod(o, name):
    ???

f = Foo()
callMethod(f, "bar1")
Jazz
źródło
1
Chociaż są one podobne, nie jest to dokładny duplikat tego pytania , które dotyczy raczej funkcji w module niż metody obiektu.
Grumdrig
Bardzo blisko spokrewniony (cholera, może nawet dupe): Jak uzyskać dostęp do atrybutu obiektu podanego ciągu odpowiadającego nazwie tego atrybutu . Metody też są atrybutami.
Aran-Fey

Odpowiedzi:

113

Użyj wbudowanej getattr()funkcji:

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

Możesz również użyć setattr()do ustawiania atrybutów klas według nazw.

Enrico Carlesso
źródło
Nie mogłem znaleźć tego, czego szukać w dokumentacji! Dzięki!
Jazz
@Jazz, to jest wbudowane. Być może będziesz musiał przeprowadzić wyszukiwanie na stronie za pomocąC-f
aaronasterling
@aaronasterling Wiem, ale nie mogłem znaleźć słowa do wyszukania!
Jazz
dlaczego callMethod (f, "bar1") nie nazywa się f.callMethod (f, "bar1")
Philip Puthenvila
PhilipJ, ponieważ jest to tylko metoda zdefiniowana kilka linii wcześniej, poza Foo: class.
Enrico Carlesso
6

Miałem podobne pytanie, chciałem wywołać metodę instancji przez odniesienie. Oto zabawne rzeczy, które znalazłem:

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 jest niesamowity!

Yaroslav Stavnichiy
źródło
2
getattr(globals()['Foo'](), 'bar1')()
getattr(globals()['Foo'](), 'bar2')()

Nie musisz najpierw tworzyć instancji Foo!

Björn
źródło
To był tylko przykład, mam prawdziwy przykład prawdziwej klasy!
Jazz
2
Wywołanie metody niezainicjowanej klasy może sugerować, że robisz coś źle.
Enrico Carlesso
a co, jeśli foonie ma go w świecie globalnym?
aaronasterling
1
prawdopodobnie nie jest, ale powinieneś uważać Foo. ;)
johndodo
2
def callmethod(cls, mtd_name):    
    method = getattr(cls, mtd_name)
    method()
Htechno
źródło
0

Oto bardziej uogólniona wersja wykorzystująca dekoratory Pythona. Możesz zadzwonić, używając krótkiej lub długiej nazwy. Znalazłem to przydatne podczas implementowania CLI z krótkimi i długimi poleceniami podrzędnymi.

Dekoratory Pythona są wspaniałe. Bruce Eckel (Thinking in Java) pięknie opisuje tutaj dekoratory Pythona.

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')
Nitin Muppalaneni
źródło