developer tip

함수, 바인딩되지 않은 메서드 및 바인딩 된 메서드의 차이점은 무엇입니까?

copycodes 2020. 12. 25. 10:21
반응형

함수, 바인딩되지 않은 메서드 및 바인딩 된 메서드의 차이점은 무엇입니까?


나는 때문에 코멘트 스레드에서 토론의이 질문을 부탁 해요 이 답변 . 나는 내 머리를 빙빙 돌릴 수있는 방법의 90 %입니다.

In [1]: class A(object):  # class named 'A'
   ...:     def f1(self): pass
   ...:
In [2]: a = A()  # an instance

f1 세 가지 다른 형태로 존재합니다.

In [3]: a.f1  # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1  # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1']  # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1']  # a function
Out[6]: <function __main__.f1>

바인딩 된 메서드 , 바인딩되지 않은 메서드함수 개체 의 차이점은 무엇입니까 ? 모두 f1에 의해 설명됩니다. 이 세 가지 물체를 어떻게 부르나요? 그들은 어떻게 서로 변할 수 있습니까? 이 항목에 대한 문서 는 이해하기 매우 어렵습니다.


기능은 바이 만든 def문, 또는에 의해 lambda. Python 2에서 함수가 class명령문 본문에 표시 되거나 type클래스 생성 호출에 전달 되면 바인딩되지 않은 메서드 로 변환됩니다 . (Python 3에는 언 바운드 메서드가 없습니다. 아래 참조) 클래스 인스턴스에서 함수에 액세스 하면 메서드에 인스턴스를 첫 번째 매개 변수 로 자동으로 제공 하는 바인딩 된 메서드 로 변환됩니다 self.

def f1(self):
    pass

여기 f1기능이 있습니다.

class C(object):
    f1 = f1

지금 C.f1은 언 바운드 방법입니다.

>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True

type클래스 생성자를 사용할 수도 있습니다 .

>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>

f1언 바운드 메서드로 수동으로 변환 있습니다.

>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>

바인딩되지 않은 메서드는 클래스 인스턴스에 대한 액세스에 의해 바인딩됩니다.

>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>

액세스는 설명자 프로토콜을 통해 호출로 변환됩니다.

>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

다음을 결합 :

>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>

또는 직접 :

>>> types.MethodType(f1, C(), C)                
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

함수와 바인딩되지 않은 메서드의 주요 차이점은 후자는 바인딩 된 클래스를 알고 있다는 것입니다. 바인딩되지 않은 메서드를 호출하거나 바인딩하려면 해당 클래스 유형의 인스턴스가 필요합니다.

>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>

함수와 바인딩되지 않은 메서드의 차이가 매우 적기 때문에 Python 3는 그 차이를 제거합니다. Python 3에서 클래스 인스턴스의 함수에 액세스하면 함수 자체가 제공됩니다.

>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True

Python 2와 Python 3 모두에서 다음 세 가지는 동일합니다.

f1(C())
C.f1(C())
C().f1()

함수를 인스턴스에 바인딩하면 첫 번째 매개 변수 (일반적으로라고 함 self)를 인스턴스 에 고정하는 효과가 있습니다. 따라서 바인딩 된 메서드 C().f1는 다음 중 하나와 동일합니다.

(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())

이해하기 꽤 어렵다

글쎄, 그것은 꽤 어려운 주제이며 설명자와 관련이 있습니다.

기능부터 시작하겠습니다. 여기에서 모든 것이 명확합니다. 호출하기 만하면 모든 제공된 인수가 실행되는 동안 전달됩니다.

>>> f = A.__dict__['f1']
>>> f(1)
1

TypeError매개 변수 수에 문제가있는 경우 Regular 가 발생합니다.

>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)

자, 방법. 메서드는 약간의 향신료가있는 함수입니다. 여기서 설명자가 게임에 등장합니다. 설명 된 바와 같이 데이터 모델 , A.f1A().f1로 번역 A.__dict__['f1'].__get__(None, A)하고 type(a).__dict__['f1'].__get__(a, type(a))각각. 그리고 이것들 __get__결과 는 원시 f1함수 와 다릅니다 . 이러한 개체는 원본에 대한 래퍼 f1이며 몇 가지 추가 논리를 포함합니다.

unbound method이 논리의 경우 첫 번째 인수가 다음의 인스턴스인지 확인합니다 A.

>>> f = A.f1
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead) 

이 검사가 성공하면 f1해당 인스턴스를 첫 번째 인수로 사용하여 원본 실행합니다 .

>>> f(A())
<__main__.A object at 0x800f238d0>

해당 im_self속성은 None다음과 같습니다.

>>> f.im_self is None
True

경우 bound method이 로직 공급 즉시 원래 f1의 인스턴스 A가 (이 경우 실제로 저장된다 작성된 im_self특성) :

>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>

따라서 bound기본 함수가 일부 인스턴스에 바인딩되어 있음을 의미합니다. unbound여전히 바인딩되어 있지만 클래스에만 적용됨을 의미합니다.


함수 개체는 함수 정의에 의해 생성 된 호출 가능한 개체입니다. 바인딩 된 메서드와 바인딩되지 않은 메서드는 모두 도트 이항 연산자가 호출하는 설명자에 의해 생성 된 호출 가능한 개체입니다.

Bound and unbound method objects have 3 main properties: im_func is the function object defined in the class, im_class is the class, and im_self is the class instance. For unbound methods, im_self is None.

When a bound method is called, it calls im_func with im_self as the first parameter followed its calling parameters. unbound methods calls the underlying function with just its calling parameters.


One interesting thing I saw today is that, when I assign a function to a class member, it becomes an unbound method. Such as:

class Test(object):
    @classmethod
    def initialize_class(cls):
        def print_string(self, str):
            print(str)
        # Here if I do print(print_string), I see a function
        cls.print_proc = print_string
        # Here if I do print(cls.print_proc), I see an unbound method; so if I
        # get a Test object o, I can call o.print_proc("Hello")

Please refer to the Python 2 and Python 3 documentation for more details.

My interpretation is the following.

Class Function snippets:

Python 3:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)

Python 2:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype)
  1. If a function is called without class or instance, it is a plain function.
  2. If a function is called from a class or an instance, its __get__ is called to retrieve wrapped function:
    a. B.x is same as B.__dict__['x'].__get__(None, B). In Python 3, this returns plain function. In Python 2, this returns an unbound function.

    b. b.x is same as type(b).__dict__['x'].__get__(b, type(b). This will return a bound method in both Python 2 and Python 3, which means self will be implicitly passed as first argument.


What is the difference between a function, an unbound method and a bound method?

From the ground breaking what is a function perspective there is no difference. Python object oriented features are built upon a function based environment.

Being bound is equal to:

Will the function take the class (cls) or the object instance (self) as the first parameter or no?

Here is the example:

class C:

    #instance method 
    def m1(self, x):
        print(f"Excellent m1 self {self} {x}")

    @classmethod
    def m2(cls, x):
        print(f"Excellent m2 cls {cls} {x}")

    @staticmethod
    def m3(x):
        print(f"Excellent m3 static {x}")    

ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)

print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)

Outputs:

Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>

The output shows the static function m3 will be never called bound. C.m2 is bound to the C class because we sent the cls parameter which is the class pointer.

ci.m1 and ci.m2 are both bound; ci.m1 because we sent self which is a pointer to the instance, and ci.m2 because the instance knows that the class is bound ;).

To conclude you can bound method to a class or to a class object, based on the first parameter the method takes. If method is not bound it can be called unbound.


Note that method may not be originally part of the class. Check this answer from Alex Martelli for more details.

ReferenceURL : https://stackoverflow.com/questions/11949808/what-is-the-difference-between-a-function-an-unbound-method-and-a-bound-method

반응형