अजगर: एक अनबाउंड विधि बांधें?


117

पायथन में, क्या बिना बुलाए एक अनबाउंड विधि को बांधने का एक तरीका है?

मैं एक 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इस के आसपास काम करने के लिए उपयोग कर रहा हूं , लेकिन क्या किसी को पता है कि क्या एक साफ-सुथरा, स्वस्थ, पाइथोनिक तरीका एक अनबाउंड विधि को एक उदाहरण के साथ बांधने के लिए है और इसे कॉल किए बिना इसे जारी रखना है?


6
@ क्रिस्टोफर - एक विधि जो उस वस्तु के दायरे से बंधी नहीं है जिसे वह चूसा गया था, इसलिए आपको स्पष्ट रूप से स्वयं को पास करना होगा।
एडेन बेल

2
मुझे विशेष रूप से "शानदार धब्बा और मैं रोता हूं।"
19-26 को jspencer

जवाबों:


174

सभी फ़ंक्शंस डिस्क्रिप्टर भी हैं , इसलिए आप उनकी __get__विधि को कॉल करके उन्हें बाँध सकते हैं :

bound_handler = handler.__get__(self, MyWidget)

यहाँ वर्णन करने के लिए R. Hettinger का उत्कृष्ट मार्गदर्शक है।


कीथ की टिप्पणी से एक आत्म निहित उदाहरण के रूप में :

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" प्राप्त कर सकते हैं।
किव

मैं MethodTypeएक पर इस समाधान को पसंद करता हूं , क्योंकि यह py3k में समान काम करता है, जबकि MethodTypeतर्क को थोड़ा बदल दिया गया है।
bgw

10
और इस प्रकार, फ़ंक्शन को वर्ग उदाहरणों से बाँधने के लिए एक फ़ंक्शन: bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))उदाहरण:class A: pass; a = A(); bind(a, bind, 'bind')
कीथ पिंसन

2
हुह, आप हर दिन कुछ नया सीखते हैं। @Kazark 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

यह साफ प्रकार से किया जा सकता है । 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 पर अजगर डॉक्स में इसका कोई संदर्भ नहीं है।
काइल वाइल्ड

6
+1, मैं अपने कोड (यानी __get__) में मैजिक फ़ंक्शन के लिए कॉल नहीं करना पसंद करता हूं । मुझे नहीं पता कि अजगर के किस संस्करण के लिए आपने यह परीक्षण किया है, लेकिन अजगर 3.4 पर, MethodTypeफ़ंक्शन दो तर्क लेता है। फ़ंक्शन और उदाहरण। इसलिए इसे बदल दिया जाना चाहिए types.MethodType(f, C())
दान मिलन

यह रहा! यह उदाहरण के तरीकों को पैच करने का एक अच्छा तरीका है:wgt.flush = types.MethodType(lambda self: None, wgt)
विनैंड जूल

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)
दान पसारो

7

यह selfकरने के लिए बाध्य होगा handler:

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

यह selfफ़ंक्शन के पहले तर्क के रूप में गुजरता है। object.function()के लिए सिंटैक्टिक शुगर है function(object)


1
हां, लेकिन यह विधि को बुलाता है। समस्या यह है कि मुझे कॉल करने योग्य वस्तु के रूप में बाध्य विधि को पारित करने में सक्षम होना चाहिए। मेरे पास अनबाउंड विधि और उदाहरण है जो मैं इसे करने के लिए बाध्य होना चाहता हूं, लेकिन यह पता नहीं लगा सकता कि इसे तुरंत कॉल किए बिना सभी को एक साथ कैसे रखा जाए
Dan Passaro

9
नहीं, ऐसा नहीं है, यह केवल पद्धति को कॉल करेगा यदि आप बाउंड_हैंडलर () करते हैं। लैम्बडा को परिभाषित करना लैम्बडा को कॉल नहीं करता है।
ब्रायन-ब्राज़ील

1
आप वास्तव में functools.partialएक मेमने को परिभाषित करने के बजाय उपयोग कर सकते हैं । यह सटीक समस्या को हल नहीं करता है, यद्यपि। आप अभी भी एक साथ काम कर रहे functionएक के बजाय instancemethod
एलन प्लम

5
@ एलन: आपके अंतर function-एड और जिनके पहले तर्क में क्या अंतर है instancemethod; बतख टाइपिंग में अंतर नहीं देखा जा सकता है।
रेयान

2
@ LieRyan अंतर यह है कि आप अभी भी मौलिक प्रकार के साथ काम नहीं कर रहे हैं। functools.partialकुछ मेटाडाटा, जैसे __module__। (इसके अलावा मैं रिकॉर्ड के लिए राज्य चाहता हूं कि जब मैं इस जवाब पर अपनी पहली टिप्पणी को देखता हूं तो मैं बहुत मुश्किल महसूस करता हूं।) वास्तव में अपने प्रश्न में मैं उल्लेख करता हूं कि मैं पहले से ही उपयोग कर रहा हूं, functools.partialलेकिन मुझे लगा कि "शुद्ध" तरीका होना चाहिए। चूंकि यह अनबाउंड और बाउंड दोनों तरीकों को प्राप्त करना आसान है।
डान पासारो

1

पार्टी के लिए देर से, लेकिन मैं एक समान प्रश्न के साथ यहां आया था: मेरे पास एक वर्ग विधि और एक उदाहरण है, और विधि के लिए उदाहरण लागू करना चाहते हैं।

ओपी के प्रश्न की देखरेख के जोखिम में, मैंने कुछ कम रहस्यमय काम किया जो दूसरों के लिए उपयोगी हो सकता है जो यहां पहुंचे (कैविएट: मैं पायथन 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 में करता है
दान पसारो

अपनी मूल आवश्यकताओं को अधिक ध्यान से न पढ़ने के लिए क्षमा याचना। मैं @ brian-brazil द्वारा stackoverflow.com/a/1015355/558639 पर दिए गए लंबोदर-आधारित दृष्टिकोण के लिए आंशिक (दंडात्मक उद्देश्य) हूं - यह उतना ही शुद्ध है जितना आप प्राप्त कर सकते हैं।
fearless_fool
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.