2013-08-20 1 views
2

개체에서 함수를 호출 할 때 파이썬은 self을 어떻게 바인딩합니까? 내가 이해하려고 노력 중이 야 이유 :파이썬에서 어떻게 자기가 바운드됩니까?

class A(object): 
    def __init__(self): 
     self.name = 'class A' 

    def show_name(self): 
     print(self, self.name) 

class B(object): 
    def __init__(self, a): 
     self.name = 'class B' 
     self.show_name = a.show_name 

A().show_name() 
B(A()).show_name() 

출력

(<self.A object at 0x7f9d35a06e50>, 'class A') 
(<self.A object at 0x7f9d35a06e50>, 'class A') 

편집 : 그것은 show_name은 첫 번째 (self) 매개 변수로 클래스 A의 인스턴스를 제공해야 함을 알 수 있을까하는 방법, 클래스 B의 인스턴스가 아닌가?

+5

이것은 몇 달 전의 질문 중 하나이지만, 찾을 수 없습니다. 저는 [블로그 게시물] (http://stupidpythonideas.blogspot.com/2013/06/how-methods-work.html)을 찾을 수 있습니다. 답변을 설명하는 데 도움이되었습니다. – abarnert

+1

@abarnert 멋진 기사. 어떤 타입 에든'__call__' 메쏘드를 추가하고 호출 가능하도록 할 수 있는지 전혀 몰랐습니다. –

+0

@ 제레미 : 필자는 파이썬 3.x를 함수로 사용하기 때문에 여기에서 파이썬 3.x를 사용하고 있다고 가정합니다. 그러나 출력을 보면 파이썬 2.x처럼 보입니다. 2.x가 어떻게 작동하는지 설명하기 위해 내 대답을 편집하길 원하십니까? (좀 더 복잡하고 특수한 속성의 이름은 다르지만 기본 아이디어는 같습니다.) – abarnert

답변

5

자세한 내용은 this blog post을 참조하십시오. 하지만 여기서 요약 할 수 있습니다.

핵심은 바인딩 된 방법을 이해하는 것입니다. 바운드 메소드는 실제 함수 오브젝트와 그것이 바인드 된 인스턴스를 보유하는 오브젝트입니다. 이런 식으로 뭔가 :

>>> class C: 
...  def foo(self): 
...   print(self) 
>>> C.foo 
<function __main__.foo> 
>>> c = C() 
>>> c.foo 
<bound method C.foo of <__main__.C object at 0x10ab90a90>> 
>>> c.foo.__func__ 
<function __main__.foo> 
>>> c.foo.__func__ is C.foo, c.foo.__self__ is c 
(True, True) 

을 그리고 당신도 수동으로 구성 할 수 있습니다 :

>>> import types 
>>> f = types.MethodType(C.foo, 2) 
>>> f 
<bound method int.foo of 2> 
>>> f() 
2 
당신이 주변을 전달하고이를 검사 할 수 있도록

class BoundMethod(object): 
    def __init__(self, function, instance): 
     self.__func__ = function 
     self.__self__ = instance 
    def __call__(self, *args, **kwargs): 
     return self.__func__(self.__self__, *args, **kwargs) 

바인딩 방법, 일류 개체

(위의 Python 클래스는 분명히 실제 코드가 아니지만 멀리 떨어져있는 것은 아니며 바인드 된 메서드 나 호출 할 수없는 메서드를 바인딩하려는 바보 같은 작업을 수행하면 대부분 동일한 오류가 발생합니다.)


따라서 c.fooc에 어떻게 끝나나요? 이를 실제로 이해하려면 설명자를 이해해야합니다. 그러나 짧은 버전은 다음과 같습니다.

거의 모든 유형의 파이썬에는 추가 방법 __get__이 있습니다. 그리고 이것은 속성 액세스에 사용됩니다. 당신이 c.foo를 입력 할 때 기본적으로,이 같은 무언가를 (기본 클래스, 슬롯, getattr 오버라이드 (override) 등 무시) :

try: 
    return c.__dict__['foo'] 
except KeyError: 
    value = type(c).__dict__['foo'] 
    try: 
     return value.__get__(c) 
    except AttributeError: 
     return value 

함수 객체의 __get__ 방법은 바인딩 된 메서드를 반환합니다 (인수에 바인딩) .


그래서, 분명히 다시 예 약 A B(A()).show_name 때문에이 A 인스턴스에 바인딩 물건을 B(A()).show_name() 인쇄 외출 ...하지만 어떻게 그런 일이?

음,이를 통해 추적하자 : 당신의 __init__ 기능 내부

>>> a = A() 
>>> b = B(a) 
>>> b.show_name 

을, 이렇게 : 단지 self.show_name 변수 일반 인스턴스에 이미 바인딩 방법 a.show_name을 복사하는 것

self.show_name = a.show_name 

. 따라서 나중에 b.show_name을 조회하면 동일한 바인딩 방법 인 a에 바인딩됩니다. 다시 바인드하려면 다음과 같이 수동으로해야합니다.

self.show_name = types.MethodType(a.show_name.__func__, self) 
+0

자, 올바르게 이해했다면, 생성자의'B' 인스턴스의'show_name' 속성에 아무 것도 지정하지 않고 건너 뛰면'types.MethodType (.) '을 할당하는 것과 같은 결과를 얻지 못할 것입니다. ..'인터프리터는'B' 인스턴스 나'B'에서'show_name'을 찾지 못하고 결국'A' 클래스에서 발견 될 때까지 룩업 체인으로 올라갈 것입니다. 속성 설명자 바인딩 된 메서드를 반환하고이 설명자가 반환 할 B 인스턴스에 바인딩 된 메서드를 검색합니다. 올바른가? –

+1

@Asad : B가 A의 하위 클래스 인 경우 그렇습니다. 그러나 OP와 내 대답은 그것들을 독립 클래스로 정의했는데, 모두'object'에서 상속되기 때문에'A'는 룩업 체인에 포함되지 않을 것입니다 (룩업 체인을'B .__ mro__'로 볼 수 있습니다. [here] (http://www.python.org/download/releases/2.3/mro/). – abarnert

+0

아, 미안. 나는 그 퀘스트를 읽지 않았다. 신중하게 B가 A에서 상속받은 것으로 추정하고있었습니다. –

4

B의 생성자에 a.show_name을 바인딩하면 바인딩됩니다. 따라서 B의 인스턴스에서 show_name을 가져 오면 이미 a에 바인딩되어 있으므로 B 인스턴스에 바인딩되지 않습니다.

+0

'B' 생성자의 바인딩이 어떻게 작동 하는지를 설명 할 수 있습니까? – Jeremy

+1

'B'의 생성자에있는 것이 구체적이지 않습니다. 'a.show_name'처럼 인스턴스에서 언 바운드 메소드를 가져올 때마다 그 인스턴스에 바인드 된 메소드를 얻습니다. –

+0

이것은 두 줄로 알아야하는 것의 90 %를 다루는 아주 간결한 설명입니다. – abarnert