Llamar a un método de Python por su nombre

81

Si tengo un objeto y un nombre de método en una cadena, ¿cómo puedo llamar al método?

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

def callMethod(o, name):
    ???

f = Foo()
callMethod(f, "bar1")
Jazz
fuente
1
Aunque son similares, esto no es un duplicado exacto de esta pregunta , que pregunta sobre una función en un módulo, en lugar de un método de un objeto.
Grumdrig
Muy estrechamente relacionado (diablos, tal vez incluso engañado): cómo acceder al atributo del objeto dada la cadena correspondiente al nombre de ese atributo . Los métodos también son atributos.
Aran-Fey

Respuestas:

113

Utilice la getattr()función incorporada:

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

También puede utilizar setattr()para establecer atributos de clase por nombres.

Enrico Carlesso
fuente
¡No pude encontrar qué buscar en la documentación! ¡Gracias!
Jazz
@Jazz, está bajo incorporados. Puede que tenga que hacer una búsqueda en la página conC-f
aaronasterling
@aaronasterling Lo sé, ¡pero no pude encontrar qué palabra buscar!
Jazz
por qué callMethod (f, "bar1") no se llama f.callMethod (f, "bar1")
Philip Puthenvila
PhilipJ, porque es solo un método definido un par de líneas antes, fuera de Foo: class.
Enrico Carlesso
6

Tenía una pregunta similar, quería llamar al método de instancia por referencia. Aquí hay cosas divertidas que encontré:

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 es asombroso!

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

¡No es necesario crear una instancia de Foo primero!

Björn
fuente
Fue solo un ejemplo, ¡tengo una instancia real de una clase real!
Jazz
2
Llamar a un método de una clase no inicializada tal vez implique que estás haciendo algo mal.
Enrico Carlesso
¿y si foono está en globales?
aaronasterling
1
probablemente no lo sea, pero debe tener cuidado de que lo Foosea. ;)
johndodo
1
def callmethod(cls, mtd_name):    
    method = getattr(cls, mtd_name)
    method()
Htechno
fuente
0

Aquí hay una versión más generalizada que usa decoradores de Python. Puede llamar por nombre corto o largo. Lo encontré útil al implementar CLI con subcomandos cortos y largos.

Los decoradores de Python son maravillosos. Bruce Eckel (Thinking in Java) describe maravillosamente a los decoradores de Python aquí.

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
fuente