Get class that defined method

Jak mogę uzyskać klasę, która zdefiniowała metodę w Pythonie?

Chciałbym aby poniższy przykład wydrukował "__main__.FooClass":

class FooClass:
    def foo_method(self):
        print "foo"

class BarClass(FooClass):
    pass

bar = BarClass()
print get_class_that_defined_method(bar.foo_method)
Author: jamylak, 2009-06-07

4 answers

import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__name__ in cls.__dict__: 
            return cls
    return None
 57
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-20 19:44:30

Dzięki Sr2222 za zwrócenie uwagi, że przegapiłem punkt...

Oto poprawione podejście, które jest podobne do Alexa, ale nie wymaga importowania czegokolwiek. Nie sądzę jednak, że jest to poprawa, chyba że istnieje ogromna hierarchia odziedziczonych klas, ponieważ to podejście zatrzymuje się, gdy tylko zostanie znaleziona Klasa definiująca, zamiast zwracać całe dziedzictwo, jak to robi getmro. Jak powiedział, Jest to bardzo mało prawdopodobny scenariusz.

def get_class_that_defined_method(method):
    method_name = method.__name__
    if method.__self__:    
        classes = [method.__self__.__class__]
    else:
        #unbound method
        classes = [method.im_class]
    while classes:
        c = classes.pop()
        if method_name in c.__dict__:
            return c
        else:
            classes = list(c.__bases__) + classes
    return None

I Przykład:

>>> class A(object):
...     def test(self): pass
>>> class B(A): pass
>>> class C(B): pass
>>> class D(A):
...     def test(self): print 1
>>> class E(D,C): pass

>>> get_class_that_defined_method(A().test)
<class '__main__.A'>
>>> get_class_that_defined_method(A.test)
<class '__main__.A'>
>>> get_class_that_defined_method(B.test)
<class '__main__.A'>
>>> get_class_that_defined_method(C.test)
<class '__main__.A'>
>>> get_class_that_defined_method(D.test)
<class '__main__.D'>
>>> get_class_that_defined_method(E().test)
<class '__main__.D'>
>>> get_class_that_defined_method(E.test)
<class '__main__.D'>
>>> E().test()
1

Rozwiązanie Alex zwraca te same wyniki. Dopóki można zastosować podejście Alex, użyłbym go zamiast tego.

 5
Author: estani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-30 13:04:15

Nie wiem, dlaczego nikt nigdy o tym nie wspomniał, ani dlaczego top answer ma 50 upvotów, gdy jest powolny jak cholera, ale możesz też zrobić następujące rzeczy:

def get_class_that_defined_method(meth):
    return meth.im_class.__name__

Dla Pythona 3 wierzę, że to się zmieniło i musisz przyjrzeć się .__qualname__.

 2
Author: Nick Chapman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-02 21:09:46

Zacząłem robić coś podobnego, w zasadzie chodziło o sprawdzanie, czy metoda w klasie bazowej została zaimplementowana, czy nie w klasie podrzędnej. Okazało się, że tak, jak pierwotnie to zrobiłem, nie mogłem wykryć, kiedy klasa pośrednia faktycznie wdrażała metodę.

Moje obejście było dość proste; ustawienie metody atrybutu i przetestowanie jej obecności później. Oto uproszczenie całości:

class A():
    def method(self):
        pass
    method._orig = None # This attribute will be gone once the method is implemented

    def run_method(self, *args, **kwargs):
        if hasattr(self.method, '_orig'):
            raise Exception('method not implemented')
        self.method(*args, **kwargs)

class B(A):
    pass

class C(B):
    def method(self):
        pass

class D(C):
    pass

B().run_method() # ==> Raises Exception: method not implemented
C().run_method() # OK
D().run_method() # OK

UPDATE: actually call method() z run_method() (czy to nie jest duch?) i przekazać wszystkie argumenty niezmodyfikowane do metody.

P. S.: Ta odpowiedź nie odpowiada bezpośrednio na pytanie. IMHO są dwa powody, dla których jeden chciałby wiedzieć, która klasa zdefiniowała metodę; pierwszy to wskazywanie palcem na klasę w kodzie debugowania (np. w obsłudze wyjątków), a drugi to określenie, czy metoda została ponownie zaimplementowana (gdzie metoda jest stubem przeznaczonym do zaimplementowania przez programistę). Ta odpowiedź rozwiązuje ten drugi sprawa w inny sposób.

 1
Author: Thomas Guyot-Sionnest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-07 07:10:34