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これを回避するために使用していますが、バインドされていないメソッドをインスタンスにバインドし、それを呼び出さずに渡し続けるための、クリーンで健康的なPythonicの方法があるかどうか誰かが知っていますか?


6
@Christopher-吸い込まれたオブジェクトのスコープにバインドされていないメソッドなので、自分自身を明示的に渡す必要があります。
エイデンベル

2
私は特に「壮観な炎と泣きます」が好きです。
jspencer

回答:


174

すべての関数は記述子でもあるので、__get__メソッドを呼び出すことでそれらをバインドできます。

bound_handler = handler.__get__(self, MyWidget)

これがR.ヘッティンガーの優れた記述子ガイドです。


キースの コメントから抜粋した自己完結型の例として:

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でも同じように機能するため、1つよりもこのソリューションが好きですが、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

2
ええと、あなたは毎日何か新しいことを学びます。@Kazark Python 3では__get__、オブジェクトパラメータから暗黙的に取得するため、少なくとも、型の指定をスキップすることもできます。最初のパラメーターがインスタンスであるかどうかに関係なく、2番目のパラメーターとして指定するタイプには違いがないため、それを指定しても何が起きるかはわかりません。だから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ドキュメントにはそれへの参照がありません。
カイルワイルド

6
+1、私は自分のコード(つまり__get__)でマジック関数を呼び出さないようにしています。これをテストしたpythonのバージョンはわかりませんが、python 3.4では、MethodType関数は2つの引数を取ります。関数とインスタンス。したがって、これはに変更する必要があります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.method = (lambda self: lambda args: self.do(args))(self)

1
はい、これは使用していた私の元の修正とほぼ同じですfunctools.partial(handler, self)
Dan Passaro 2018

7

これは以下にバインドselfhandlerます:

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

これselfは、関数の最初の引数として渡すことで機能します。object.function()は単にの構文糖ですfunction(object)


1
はい、しかし、これはメソッドを呼び出します。問題は、バインドされたメソッドを呼び出し可能なオブジェクトとして渡すことができる必要があることです。私はバインドされていないメソッドとバインドしたいインスタンスを持っていますが、すぐにそれを呼び出さずにすべてをまとめる方法を理解できません
Dan Passaro

9
いいえ、そうではありません。bound_handler()を実行した場合にのみ、メソッドが呼び出されます。ラムダを定義してもラムダは呼び出されません。
brian-brazil 09年

1
functools.partialラムダを定義する代わりに実際に使用できます。ただし、正確な問題は解決されません。あなたはまだのfunction代わりにを扱っていますinstancemethod
アランプラム

5
@Alan:違いは何functionその最初の引数あなたの部分EDとinstancemethod; アヒルのタイピングでは違いがわかりません。
リーライアン

2
@LieRyanの違いは、基本的なタイプをまだ扱っていないことです。functools.partial一部のメタデータを削除し__module__ます。(また、私はこの答えに私の最初のコメントを見ると、私はハード本当うんざりレコードの状態WANNA。)実際には私の質問に私はすでに使用している言及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)-しかし、メソッドを渡す必要がなく、その場でそれを呼び出します。また、このは、Python 2と同じように動作し、それは、Python 3の場合のように
ダンPassaro

元の要件を注意深く読んでいないことをお詫びします。私は、stackoverflow.com / a / 1015355/558639の@ brian-brazilによって与えられたラムダベースのアプローチに部分的に(しゃれた)です-それはあなたが得ることができるのと同じくらい純粋です。
fearless_fool
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.