메서드를 정의한 클래스 가져 오기


89

파이썬에서 메서드를 정의한 클래스를 어떻게 얻을 수 있습니까?

다음 예제에서 " __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)

어떤 버전의 Python을 사용하고 있습니까? 2.2 이전에는 im_class를 사용할 수 있었지만 바인딩 된 self 객체의 유형을 표시하도록 변경되었습니다.
Kathy Van Stone

1
알아 둘만 한. 하지만 2.6을 사용하고 있습니다.
Jesse Aldridge

답변:


71
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

1
감사합니다.이 문제를 스스로 알아내는 데 시간이 좀 걸렸을 것입니다
David

모든 클래스가 구현하는 것은 아닙니다 __dict__. 때때로 __slots__사용됩니다. getattr메서드가 클래스에 있는지 테스트하는 데 사용 하는 것이 좋습니다 .
Codie CodeMonkey

16
에 대해서는 이 답변을Python 3 참조하십시오 .
Yoel 2014-09-21

27
나는 다음을 받고있다 :'function' object has no attribute 'im_class'
Zitrax 2015 년

5
Python 2.7에서는 작동하지 않습니다. 'im_class'누락에 대한 동일한 오류.
RedX

8

내가 요점을 놓치고 있다는 것을 지적 해 주신 Sr2222에게 감사드립니다 ...

다음은 Alex와 비슷하지만 아무것도 가져올 필요가없는 수정 된 접근 방식입니다. 이 접근 방식은 전체 상속을 반환하는 대신 정의 클래스가 발견 되 자마자 중지되므로 상속 된 클래스의 거대한 계층 구조가없는 한 개선이라고 생각 getmro하지 않습니다. 말했듯이 이것은 매우 드문 시나리오입니다.

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

그리고 예 :

>>> 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

Alex 솔루션은 동일한 결과를 반환합니다. Alex 접근 방식을 사용할 수있는 한이 방법 대신 사용합니다.


Cls().meth.__self__Cls특정 인스턴스에 바인딩 된 인스턴스를 제공합니다 meth. 그것은 유사입니다 Cls().meth.im_class. 당신이있는 경우 class SCls(Cls), SCls().meth.__self__당신을 얻을 것이다 SCls인스턴스가 아닌 Cls인스턴스를. OP가 원하는 것은 Cls@Alex Martelli가하는 것처럼 MRO를 걸어야 만 사용할 수있는 것으로 보입니다.
Silas Ray

@ sr2222 당신이 맞습니다. Alex 솔루션이 더 컴팩트하다고 생각하지만 이미 시작 했으므로 대답을 수정했습니다.
estani

1
수입을 피해야하는 경우 좋은 솔루션이지만 기본적으로 MRO를 다시 구현하는 것이므로 영원히 작동한다고 보장 할 수는 없습니다. MRO는 아마도 동일하게 유지 될 것이지만, 파이썬의 과거에 이미 한 번 변경되었으며, 다시 변경되면이 코드는 미묘하고 널리 퍼진 버그가됩니다.
Silas Ray

반복해서 "아주 가능성이없는 시나리오"가 프로그래밍에서 발생합니다. 드물게 재난을 유발합니다. 일반적으로 "XY.Z %는 절대 일어나지 않을 것"이라는 사고 패턴 은 코딩을 할 때 매우 형편없는 사고 도구입니다. 그것을 사용하지 마십시오. 100 % 올바른 코드를 작성하십시오.
ulidtko

@ulidtko 설명을 잘못 읽은 것 같습니다. 정확성이 아니라 속도입니다. 모든 경우에 적합한 "완벽한"솔루션은 없습니다. 그렇지 않으면 정렬 알고리즘이 하나만있을 것입니다. 여기에 제안 된 솔루션은 "드문"경우에 더 빠릅니다. 가독성 이후 모든 경우의 99 % 비율로 속도가 나오기 때문에이 솔루션은 드물게 "유일한"경우에만 더 나은 솔루션이 될 수 있습니다. 당신이 그것을 읽지 않았다면, 그 코드는 당신이 두려워했던 것이라면 100 % 정확합니다.
estani

6

나는 왜 아무도 이것을 제기하지 않았는지 또는 지옥처럼 느릴 때 상위 답변에 50 개의 upvotes가있는 이유를 모르겠지만 다음을 수행 할 수도 있습니다.

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

파이썬 3의 경우 이것이 변경되었다고 생각하며 .__qualname__.


2
흠. 파이썬 2.7에서 정의하는 클래스를 볼 수 없습니다.-메서드가 정의 된 클래스가 아니라 호출 된 클래스를 받고 있습니다 ...
F1Rumors

5

Python 3에서 실제 클래스 객체가 필요한 경우 다음을 수행 할 수 있습니다.

import sys
f = Foo.my_function
vars(sys.modules[f.__module__])[f.__qualname__.split('.')[0]]  # Gets Foo object

함수가 중첩 된 클래스에 속할 수있는 경우 다음과 같이 반복해야합니다.

f = Foo.Bar.my_function
vals = vars(sys.modules[f.__module__])
for attr in f.__qualname__.split('.')[:-1]:
    vals = vals[attr]
# vals is now the class Foo.Bar

1

나는 다소 비슷한 일을 시작했다. 기본적으로 기본 클래스의 메서드가 하위 클래스에 구현되었는지 여부를 확인하는 것이 었습니다. 내가 원래했던 방식으로 밝혀 졌는데 중간 클래스가 실제로 메서드를 구현하고있을 때 감지 할 수 없었습니다.

내 해결 방법은 실제로 매우 간단했습니다. 메소드 속성을 설정하고 나중에 그 존재를 테스트합니다. 다음은 전체를 단순화 한 것입니다.

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

업데이트 : 실제로 (정신이 아닌가?) method()에서 호출 하고 run_method()수정되지 않은 모든 인수를 메서드에 전달합니다.

추신 :이 답변은 질문에 직접 답하지 않습니다. IMHO 메서드를 정의한 클래스를 알고 싶은 두 가지 이유가 있습니다. 첫 번째는 디버그 코드 (예 : 예외 처리)에서 클래스를 가리키는 손가락이고, 두 번째는 메서드가 다시 구현되었는지 확인하는 것입니다 (여기서 메서드는 프로그래머가 구현할 스텁). 이 답변은 두 번째 경우를 다른 방식으로 해결합니다.


1

파이썬 3

매우 간단한 방법으로 해결했습니다.

str(bar.foo_method).split(" ", 3)[-2]

이것은 준다

'FooClass.foo_method'

점으로 분할하여 클래스와 함수 이름을 개별적으로 가져옵니다.


와우!
user3712978
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.