Python : 바인딩되지 않은 메서드 바인딩?


117

Python에서 바인딩되지 않은 메서드를 호출하지 않고 바인딩하는 방법이 있습니까?

저는 wxPython 프로그램을 작성 중이며 특정 클래스의 경우 모든 버튼의 데이터를 클래스 수준의 튜플 목록으로 함께 그룹화하는 것이 좋습니다.

class MyWidget(wx.Window):
    buttons = [("OK", OnOK),
               ("Cancel", OnCancel)]

    # ...

    def Setup(self):
        for text, handler in MyWidget.buttons:

            # This following line is the problem line.
            b = wx.Button(parent, label=text).Bind(wx.EVT_BUTTON, handler)

문제는의 모든 값 handler이 바인딩되지 않은 메서드이기 때문에 내 프로그램이 눈부시게 폭발하고 눈물을 흘린다는 것입니다.

저는 온라인에서 비교적 간단하고 해결 가능한 문제에 대한 해결책을 찾고있었습니다. 불행히도 나는 아무것도 찾을 수 없었다. 지금 functools.partial은이 문제를 해결하기 위해 사용 하고 있지만 바인딩되지 않은 메서드를 인스턴스에 바인딩하고 호출하지 않고 계속 전달하는 깔끔하고 건강한 Python 방식이 있는지 아는 사람이 있습니까?


6
@Christopher-빨아 들인 객체의 범위에 구속되지 않는 메서드이므로 self를 명시 적으로 전달해야합니다.
Aiden Bell

2
나는 특히 "화려한 불꽃과 나는 눈물"을 좋아한다.
jspencer

답변:


174

모든 함수도 설명자 이므로 해당 __get__메서드 를 호출하여 바인딩 할 수 있습니다 .

bound_handler = handler.__get__(self, MyWidget)

다음은 R. Hettinger의 훌륭한 설명자 가이드 입니다.


Keith의 의견 에서 가져온 독립형 예 :

def bind(instance, func, as_name=None):
    """
    Bind the function *func* to *instance*, with either provided name *as_name*
    or the existing name of *func*. The provided *func* should accept the 
    instance as the first argument, i.e. "self".
    """
    if as_name is None:
        as_name = func.__name__
    bound_method = func.__get__(instance, instance.__class__)
    setattr(instance, as_name, bound_method)
    return bound_method

class Thing:
    def __init__(self, val):
        self.val = val

something = Thing(21)

def double(self):
    return 2 * self.val

bind(something, double)
something.double()  # returns 42

3
꽤 괜찮은데. 유형을 생략하고 대신 "바운드 메서드? .f"를 반환하는 방법이 마음에 듭니다.
Kiv

이 솔루션이 MethodTypepy3k에서 동일하게 작동하지만 MethodType의 인수가 약간 변경 되었기 때문에이 솔루션이 마음 에 듭니다 .
bgw 2011 년

10
따라서 함수를 클래스 인스턴스에 바인딩하는 함수 : bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))예 :class A: pass; a = A(); bind(a, bind, 'bind')
Keith Pinson 2011-08-13

2
허, 당신은 매일 새로운 것을 배웁니다. @Kazark Python 3에서는 __get__객체 매개 변수에서 암시 적으로 가져 오므로 최소한 유형 제공을 건너 뛸 수도 있습니다 . 첫 번째 매개 변수의 인스턴스에 관계없이 두 번째 매개 변수로 제공하는 유형에 차이가 없으므로 제공하는 것이 아무 일도 수행하지 않는지 확실하지 않습니다. 따라서 bind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))트릭도해야합니다. ( bind개인적으로 데코레이터로 사용할 수있는 것을 선호 하지만 그것은 다른 문제입니다.)
JAB

1
와, 함수가 디스크립터라는 것을 알지 못했습니다. 그것은 매우 우아한 디자인이고, 메서드는 클래스의 평범한 함수 __dict__이며 속성 액세스는 일반 설명자 프로토콜을 통해 바인딩되지 않거나 바인딩 된 메서드를 제공합니다. 나는 항상 동안 무슨 일이 있었 마법의 일종이었다 가정type.__new__()
JaredL

81

이것은 types.MethodType으로 깔끔하게 수행 할 수 있습니다 . 예:

import types

def f(self): print self

class C(object): pass

meth = types.MethodType(f, C(), C) # Bind f to an instance of C
print meth # prints <bound method C.f of <__main__.C object at 0x01255E90>>

10
+1 이것은 훌륭하지만 제공 한 URL의 Python 문서에는 참조가 없습니다.
Kyle Wild

6
+1, 내 코드 (예 :)에 매직 함수를 호출하지 않는 것을 선호합니다 __get__. 나는 이것을 테스트 한 파이썬 버전을 모르지만 파이썬 3.4에서 MethodType함수는 두 개의 인수를 취합니다. 함수와 인스턴스. 따라서 이것은 types.MethodType(f, C()).
Dan Milon 2014 년

여기있어! 인스턴스 메소드를 패치하는 좋은 방법입니다.wgt.flush = types.MethodType(lambda self: None, wgt)
Winand

2
실제로 문서에 언급되어 있지만 다른 답변의 설명자 페이지에 있습니다. docs.python.org/3/howto/descriptor.html#functions-and-methods
kai

9

self가있는 클로저를 생성하면 기능이 기술적으로 바인딩되지는 않지만 동일한 (또는 매우 유사한) 근본적인 문제를 해결하는 다른 방법입니다. 다음은 간단한 예입니다.

self.method = (lambda self: lambda args: self.do(args))(self)

1
예,이에 대해 사용하는 것이었다 내 원래의 수정,과 동일합니다functools.partial(handler, self)
댄 Passaro

7

이것은 다음에 바인딩 self됩니다 handler.

bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs)

이것은 self함수에 대한 첫 번째 인수로 전달 하여 작동합니다. object.function()의 구문 상 설탕입니다 function(object).


1
예, 그러나 이것은 메소드를 호출합니다. 문제는 바인딩 된 메서드를 호출 가능한 개체로 전달할 수 있어야한다는 것입니다. 나는 언 바운드 방법을 가지고 인스턴스가 나는 그것이에 바인딩 할 싶지만 즉시 호출하지 않고 모두 함께 넣어하는 방법을 알아낼 수 없습니다
댄 Passaro을

9
아니요, 그렇지 않습니다. bound_handler ()를 수행하는 경우에만 메서드를 호출합니다. 람다를 정의하는 것은 람다를 호출하지 않습니다.
brian-brazil

1
functools.partial람다를 정의하는 대신 실제로 사용할 수 있습니다 . 하지만 정확한 문제를 해결하지는 못합니다. 당신은 여전히 상대하고 function대신의 instancemethod.
Alan Plum

5
@Alan : function당신이 부분적으로 만든 첫 번째 인수와 의 차이점은 무엇입니까 instancemethod; 오리 타이핑은 차이를 볼 수 없습니다.
Lie Ryan

2
@LieRyan 차이점은 여전히 ​​기본 유형을 다루지 않는다는 것입니다. functools.partial일부 메타 데이터를 삭제합니다 (예 : __module__. (또한 나는이 답변에 대한 첫 번째 의견을 볼 때 정말 열심히 움츠 리는 기록을 진술하고 싶습니다.) 사실 내 질문에서 나는 이미 사용 functools.partial하고 있다고 언급 했지만 "더 순수한"방법이 있어야한다고 느꼈습니다. 바인딩되지 않은 메서드와 바인딩 된 메서드를 모두 쉽게 얻을 수 있기 때문입니다.
Dan Passaro 2015 년

1

파티에 늦었지만 비슷한 질문으로 여기에 왔습니다. 클래스 메서드와 인스턴스가 있고 인스턴스를 메서드에 적용하고 싶습니다.

OP의 질문을 과도하게 단순화 할 위험에 처한 나는 여기에 도착한 다른 사람들에게 유용 할 수있는 덜 신비한 일을하게되었습니다 (주의 : 저는 Python 3-YMMV에서 작업 중입니다).

이 간단한 클래스를 고려하십시오.

class Foo(object):

    def __init__(self, value):
        self._value = value

    def value(self):
        return self._value

    def set_value(self, value):
        self._value = value

다음과 같이 할 수 있습니다.

>>> meth = Foo.set_value   # the method
>>> a = Foo(12)            # a is an instance with value 12
>>> meth(a, 33)            # apply instance and method
>>> a.value()              # voila - the method was called
33

이것은 내 문제를 해결하지 못합니다. 즉 meth, a인수 를 보내지 않고 호출 할 수 있기 를 원했기 때문 입니다 (이것이 처음에 사용 된 이유입니다 functools.partial).하지만 메서드를 전달할 필요가없고 그냥 그 자리에서 호출하십시오. 또한이 파이썬 2와 동일한 방식으로 작동 파이썬 3에서와 같이
댄 Passaro

원래 요구 사항을 더주의 깊게 읽지 않은 것에 대해 사과드립니다. 나는 stackoverflow.com/a/1015355/558639 에서 @ brian-brazil이 제공 한 람다 기반 접근 방식에 부분적입니다 (말장난 의도) . 얻을 수있는 한 순수합니다.
fearless_fool
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.