Rufen Sie eine Python-Methode beim Namen auf

81

Wie kann ich die Methode aufrufen, wenn ich ein Objekt und einen Methodennamen in einer Zeichenfolge habe?

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

def callMethod(o, name):
    ???

f = Foo()
callMethod(f, "bar1")
Jazz
quelle
1
Obwohl sie ähnlich sind, handelt es sich nicht um ein genaues Duplikat dieser Frage , bei der eher nach einer Funktion in einem Modul als nach einer Methode eines Objekts gefragt wird.
Grumdrig
Sehr eng verwandt (zum Teufel, vielleicht sogar betrogen): Wie man auf ein Objektattribut zugreift , dessen Zeichenfolge dem Namen dieses Attributs entspricht . Methoden sind auch Attribute.
Aran-Fey

Antworten:

113

Verwenden Sie die integrierte getattr()Funktion:

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

Sie können auch setattr()Klassenattribute nach Namen festlegen.

Enrico Carlesso
quelle
Ich konnte nicht finden, wonach ich in der Dokumentation suchen sollte! Vielen Dank!
Jazz
@ Jazz, es ist unter Builtins. Möglicherweise müssen Sie eine In-Page-Suche mitC-f
aaronasterling
@aaronasterling Ich weiß, aber ich konnte nicht finden, welches Wort zu suchen!
Jazz
warum callMethod (f, "bar1") nicht f.callMethod (f, "bar1") heißt
Philip Puthenvila
PhilipJ, weil es nur eine Methode ist, die ein paar Zeilen früher außerhalb von Foo: class definiert wurde.
Enrico Carlesso
6

Ich hatte eine ähnliche Frage und wollte die Instanzmethode als Referenz aufrufen. Hier sind lustige Dinge, die ich gefunden habe:

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 ist unglaublich!

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

Sie müssen Foo nicht zuerst instanziieren!

Björn
quelle
Es war nur ein Beispiel, ich habe eine echte Instanz einer echten Klasse!
Jazz
2
Wenn Sie eine Methode einer nicht initialisierten Klasse aufrufen, bedeutet dies möglicherweise, dass Sie etwas falsch machen.
Enrico Carlesso
Was ist, wenn fooes nicht global ist?
Aaronasterling
1
es ist wahrscheinlich nicht, aber Sie sollten darauf achten Foo. ;)
Johndodo
1
def callmethod(cls, mtd_name):    
    method = getattr(cls, mtd_name)
    method()
Htechno
quelle
0

Hier ist eine allgemeinere Version mit Python-Dekoratoren. Sie können mit kurzem oder langem Namen anrufen. Ich fand es nützlich, wenn CLI mit kurzen und langen Unterbefehlen implementiert wurde.

Python-Dekorateure sind wunderbar. Bruce Eckel (Thinking in Java) beschreibt Python-Dekorateure hier sehr schön.

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
quelle