मौजूदा वस्तु उदाहरण के लिए एक विधि जोड़ना


642

मैंने पढ़ा है कि पायथन में एक मौजूदा ऑब्जेक्ट (यानी, क्लास की परिभाषा में नहीं) में एक विधि जोड़ना संभव है।

मैं समझता हूं कि ऐसा करना हमेशा अच्छा नहीं होता। लेकिन कोई ऐसा कैसे कर सकता है?

जवाबों:


921

पायथन में, कार्यों और बाध्य विधियों के बीच अंतर है।

>>> def foo():
...     print "foo"
...
>>> class A:
...     def bar( self ):
...         print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>

बाउंड विधियाँ एक उदाहरण के लिए "बाउंड" (कैसे वर्णनात्मक) हैं, और उस उदाहरण को पहले तर्क के रूप में पारित किया जाएगा जब भी विधि को बुलाया जाएगा।

एक वर्ग की विशेषताएँ (उदाहरण के विपरीत) अभी भी अनबाउंड हैं, हालाँकि, जब भी आप चाहें तो आप क्लास की परिभाषा को संशोधित कर सकते हैं:

>>> def fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

पहले से परिभाषित उदाहरणों को भी अपडेट किया जाता है (जब तक कि उन्होंने स्वयं विशेषता को ओवरराइड नहीं किया है):

>>> a.fooFighters()
fooFighters

समस्या तब आती है जब आप किसी विधि को किसी एकल उदाहरण से जोड़ना चाहते हैं:

>>> def barFighters( self ):
...     print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)

जब यह सीधे एक उदाहरण से जुड़ा होता है तो फ़ंक्शन स्वचालित रूप से बाध्य नहीं होता है:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

इसे बांधने के लिए, हम टाइप मॉड्यूल में मेथडाइप फ़ंक्शन का उपयोग कर सकते हैं :

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

इस बार कक्षा के अन्य उदाहरण प्रभावित नहीं हुए हैं:

>>> a2.barFighters()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'

अधिक जानकारी डिस्क्रिप्टर और मेटाक्लास प्रोग्रामिंग के बारे में पढ़कर पाई जा सकती है ।


65
मैन्युअल रूप से एक बनाने के बजाय MethodType, डिस्क्रिप्टर प्रोटोकॉल को मैन्युअल रूप से लागू करें और फ़ंक्शन को आपके उदाहरण का उत्पादन करें: barFighters.__get__(a)बाध्य करने के लिए एक बाध्य विधि का उत्पादन करता barFightersहै a
मार्टिज़न पीटर

2
@MartijnPiet descriptor protocolबनाम का उपयोग करने के किसी भी लाभ के एक MethodTypeतरफ एक और अधिक पठनीय होने के नाते पैदा कर सकता है।
एन्डरमैनएपीएम

17
@EndermanAPM: कई: यह ठीक उसी तरह काम करना जारी रखने की अधिक संभावना है, जो किसी इंस्टेंस पर विशेषता तक पहुँच प्राप्त करता है। यह के लिए काम करेंगे classmethodऔर staticmethodऔर अन्य वर्णनकर्ता भी। यह अभी तक एक और आयात के साथ नाम स्थान को अव्यवस्थित करने से बचता है।
मार्टिज़न पीटर

34
सुझाए गए वर्णनात्मक दृष्टिकोण के लिए पूर्ण कोड हैa.barFighters = barFighters.__get__(a)
eqzx

98

पायथन 2.6 से मॉड्यूल नया निकाला गया है और 3.0 में हटा दिया गया है, प्रकार का उपयोग करें

http://docs.python.org/library/new.html देखें

नीचे दिए गए उदाहरण में मैंने patch_me()फ़ंक्शन से जानबूझकर रिटर्न वैल्यू हटा दी है। मुझे लगता है कि रिटर्न वैल्यू देने से कोई यह विश्वास कर सकता है कि पैच एक नई वस्तु देता है, जो सच नहीं है - यह आने वाले को संशोधित करता है। संभवतः यह मंकीपैकिंग के अधिक अनुशासित उपयोग की सुविधा प्रदान कर सकता है।

import types

class A(object):#but seems to work for old style objects too
    pass

def patch_me(target):
    def method(target,x):
        print "x=",x
        print "called from", target
    target.method = types.MethodType(method,target)
    #add more if needed

a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>  
patch_me(a)    #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6)        #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>

1
यदि स्वयं को संदर्भित करने के लिए विधि को जोड़ा जा रहा है तो यह कैसे काम करेगा? मेरी पहली कोशिश एक वाक्यविन्यास त्रुटि की ओर ले जाती है, लेकिन विधि की परिभाषा में स्वयं को जोड़ना काम नहीं लगता है। आयात प्रकार वर्ग A (ऑब्जेक्ट): # लेकिन पुराने स्टाइल ऑब्जेक्ट्स के लिए भी काम करने लगता है ax = 'ax' पास def पैच_me (लक्ष्य): def मेथड (लक्ष्य, x): प्रिंट (self.ax) प्रिंट ("x =" , x) प्रिंट ("से पुकारा गया", लक्ष्य) target.method = types.MethodType (विधि, लक्ष्य) #add अधिक यदि आवश्यक हो तो a = A () प्रिंट (a.ax)
WesR

84

प्रस्तावना - संगतता पर एक नोट: अन्य उत्तर केवल पायथन 2 में काम कर सकते हैं - यह उत्तर पायथन 2 और 3 में पूरी तरह से काम करना चाहिए। यदि केवल 3 पायथन लिख रहा है, तो आप स्पष्ट रूप से विरासत में छोड़ सकते हैं object, लेकिन अन्यथा कोड समान रहना चाहिए ।

मौजूदा वस्तु उदाहरण के लिए एक विधि जोड़ना

मैंने पढ़ा है कि पायथन में मौजूदा ऑब्जेक्ट (उदाहरण के लिए वर्ग परिभाषा में नहीं) के लिए एक विधि जोड़ना संभव है।

मैं समझता हूं कि ऐसा करना हमेशा अच्छा निर्णय नहीं होता है। लेकिन, कोई ऐसा कैसे कर सकता है?

हां, यह संभव है - लेकिन अनुशंसित नहीं है

मैं इसकी अनुशंसा नहीं करता। यह विचार अच्छा नहीं है। यह मत करो।

यहाँ कारणों की एक जोड़ी है:

  • आप ऐसा करने वाले प्रत्येक उदाहरण में एक बाउंड ऑब्जेक्ट जोड़ेंगे। यदि आप यह बहुत करते हैं, तो आप शायद बहुत सारी मेमोरी बर्बाद कर देंगे। बाउंड विधियां आमतौर पर केवल उनके कॉल की छोटी अवधि के लिए बनाई जाती हैं, और जब स्वचालित रूप से कचरा एकत्र हो जाता है, तो वे अस्तित्व में रहते हैं। यदि आप इसे मैन्युअल रूप से करते हैं, तो आपके पास बाध्य पद्धति को संदर्भित करने वाला एक नाम बंधन होगा - जो उपयोग पर इसके कचरा संग्रह को रोक देगा।
  • किसी दिए गए प्रकार के ऑब्जेक्ट उदाहरणों में आम तौर पर उस प्रकार की सभी वस्तुओं पर इसके तरीके होते हैं। यदि आप कहीं और तरीके जोड़ते हैं, तो कुछ उदाहरणों में वे तरीके होंगे और अन्य नहीं होंगे। प्रोग्रामर यह उम्मीद नहीं करेंगे, और आप कम से कम आश्चर्य के नियम का उल्लंघन करने का जोखिम उठाते हैं ।
  • चूंकि ऐसा करने के लिए वास्तव में अन्य अच्छे कारण हैं, यदि आप ऐसा करते हैं तो आप खुद को एक खराब प्रतिष्ठा देंगे।

इस प्रकार, मेरा सुझाव है कि आप ऐसा नहीं करते हैं जब तक कि आपके पास वास्तव में अच्छा कारण न हो। कक्षा की परिभाषा में सही विधि को परिभाषित करना बेहतर है या कक्षा को सीधे इस तरह से बंदर-पैच करने के लिए अधिमानतः कम करना है:

Foo.sample_method = sample_method

चूंकि यह शिक्षाप्रद है, हालांकि, मैं आपको ऐसा करने के कुछ तरीके दिखाने जा रहा हूं।

यह कैसे किया जा सकता है

यहाँ कुछ सेटअप कोड है। हमें एक वर्ग की परिभाषा चाहिए। यह आयात किया जा सकता है, लेकिन यह वास्तव में कोई फर्क नहीं पड़ता।

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''

एक उदाहरण बनाएँ:

foo = Foo()

इसे जोड़ने के लिए एक विधि बनाएँ:

def sample_method(self, bar, baz):
    print(bar + baz)

विधि शून्य (0) - वर्णनकर्ता विधि का उपयोग करें, __get__

फ़ंक्शन पर बिंदीदार लुकअप फ़ंक्शन की __get__विधि को उदाहरण के साथ कॉल करते हैं , ऑब्जेक्ट को विधि से बांधते हैं और इस प्रकार "बाध्य विधि" बनाते हैं।

foo.sample_method = sample_method.__get__(foo)

और अब:

>>> foo.sample_method(1,2)
3

विधि एक - type.MethodType

सबसे पहले, आयात प्रकार, जिसमें से हमें विधि निर्माता मिलेगा:

import types

अब हम उदाहरण के लिए विधि को जोड़ते हैं। ऐसा करने के लिए, हमें typesमॉड्यूल से मेथडाइप कंस्ट्रक्टर की आवश्यकता होती है (जिसे हमने ऊपर आयात किया था)।

Type.MethodType के लिए तर्क हस्ताक्षर है (function, instance, class):

foo.sample_method = types.MethodType(sample_method, foo, Foo)

और उपयोग:

>>> foo.sample_method(1,2)
3

विधि दो: शाब्दिक बंधन

सबसे पहले, हम एक रैपर फ़ंक्शन बनाते हैं जो उदाहरण के लिए विधि को बांधता है:

def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn

उपयोग:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3

विधि तीन: functionalools.partial

एक आंशिक फ़ंक्शन एक फ़ंक्शन (और वैकल्पिक रूप से कीवर्ड तर्क) के लिए पहला तर्क (ओं) को लागू करता है, और बाद में शेष तर्कों (और कीवर्ड तर्क को ओवरराइड करने) के साथ बुलाया जा सकता है। इस प्रकार:

>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3    

यह समझ में आता है जब आप मानते हैं कि बाध्य तरीके उदाहरण के आंशिक कार्य हैं।

ऑब्जेक्ट विशेषता के रूप में अनबाउंड फ़ंक्शन - यह काम क्यों नहीं करता है:

यदि हम नमूना_मिथोड को उसी तरह से जोड़ने का प्रयास करते हैं जैसे हम इसे कक्षा में जोड़ सकते हैं, तो यह उदाहरण से अनबाउंड है, और पहले तर्क के रूप में निहित स्वयं को नहीं लेता है।

>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)

हम स्पष्ट रूप से उदाहरण (या कुछ भी, क्योंकि यह विधि वास्तव में selfतर्क चर का उपयोग नहीं करती है) द्वारा अनबाउंड फ़ंक्शन कार्य कर सकती है , लेकिन यह अन्य उदाहरणों के अपेक्षित हस्ताक्षर के अनुरूप नहीं होगा (यदि हम बंदर-पैचिंग कर रहे हैं यह उदाहरण):

>>> foo.sample_method(foo, 1, 2)
3

निष्कर्ष

अब आप कई तरीकों से जानते हैं कि आप ऐसा कर सकते हैं, लेकिन पूरी गंभीरता से - ऐसा न करें।


1
अस्वीकरण क्या मैं के बारे में सोच रहा था है। विधियाँ परिभाषा केवल वर्ग परिभाषा के भीतर निहित कार्य हैं।
एटकोल्ड

1
@ एटाल्ड मैंने परिचय में ऐसा करने से बचने के कारणों पर विस्तार किया है।
हारून हॉल

__get__विधि को भी अगले पैरामीटर के रूप में वर्ग की जरूरत है: sample_method.__get__(foo, Foo)
अनीस बेंडोराइटिस

2
@AidasBendoraitis मैं इसे "जरूरतों" के लिए नहीं कहूंगा, यह एक वैकल्पिक पैरामीटर है जिसे डिस्क्रिप्टर प्रोटोकॉल को लागू करते समय आपूर्ति की जाती है - लेकिन पायथन फ़ंक्शन तर्क का उपयोग नहीं करते हैं: github.com/python/cpython/clob/master/Objects/funcobject ग # L581
हारून हॉल

मेरी टिप्पणी इस संदर्भ पर आधारित थी: python-reference.readthedocs.io/en/latest/docs/dunderdsc/… अब मैं आधिकारिक डॉक्स से क्या देखता हूं, यह वैकल्पिक है: docs.python.org/3/howto/descriptor.html# डिस्क्रिप्टर-प्रोटोकॉल
ऐसिड बेंडोराइटिस

35

मुझे लगता है कि उपरोक्त उत्तर महत्वपूर्ण बिंदु से चूक गए।

चलो एक विधि के साथ एक वर्ग है:

class A(object):
    def m(self):
        pass

अब, आइपीथन में इसके साथ खेलते हैं:

In [2]: A.m
Out[2]: <unbound method A.m>

ठीक है, तो मी () किसी तरह की एक अनबाउंड विधि बन जाती है । लेकिन क्या वास्तव में ऐसा है?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

यह पता चला है कि एम () केवल एक फ़ंक्शन है, जिसके संदर्भ को श्रेणी के शब्दकोश में जोड़ा जाता है - कोई जादू नहीं है। फिर एम हमें एक अनबाउंड विधि क्यों देता है? इसका कारण यह है कि डॉट को सरल शब्दकोश लुकअप में अनुवादित नहीं किया गया है। यह वास्तविक रूप से A .__ वर्ग __.__ की पुकार है __ (A, 'm'):

In [11]: class MetaA(type):
   ....:     def __getattribute__(self, attr_name):
   ....:         print str(self), '-', attr_name

In [12]: class A(object):
   ....:     __metaclass__ = MetaA

In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

अब, मुझे यकीन नहीं है कि मेरे सिर के ऊपर से आखिरी पंक्ति दो बार क्यों छपी है, लेकिन फिर भी यह स्पष्ट है कि वहां क्या चल रहा है।

अब, डिफ़ॉल्ट __getattribute__ क्या करता है, यह जांचता है कि क्या विशेषता एक तथाकथित डिस्क्रिप्टर है या नहीं, यदि यह एक विशेष __get__ विधि को लागू करता है। यदि यह उस विधि को लागू करता है, तो जो लौटा है वह उस __get__ विधि को कॉल करने का परिणाम है। हमारे वर्ग के पहले संस्करण पर वापस जाना , यही हमारे पास है:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

और क्योंकि पायथन फ़ंक्शंस डिस्क्रिप्टर प्रोटोकॉल को लागू करते हैं, अगर उन्हें किसी ऑब्जेक्ट की ओर से बुलाया जाता है, तो वे अपने आप को अपने __get__ विधि में उस ऑब्जेक्ट से बांधते हैं।

ठीक है, तो किसी मौजूदा वस्तु में विधि कैसे जोड़ें? यह मानते हुए कि आप पैचिंग क्लास को बुरा नहीं मानते, यह उतना ही सरल है:

B.m = m

फिर बी.एम. "डिस्क्रिप्टर मैजिक की बदौलत" एक अनबाउंड तरीका बन जाता है।

और यदि आप किसी एकल ऑब्जेक्ट में सिर्फ एक विधि जोड़ना चाहते हैं, तो आपको type.MethotTech का उपयोग करके, मशीनरी का अनुकरण करना होगा:

b.m = types.MethodType(m, b)

वैसे:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>

19

पायथन बंदर में पैचिंग आम तौर पर अपने स्वयं के साथ एक वर्ग या फ़ंक्शन हस्ताक्षर को ओवरराइट करके काम करता है। नीचे Zope Wiki से एक उदाहरण दिया गया है :

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
   return "ook ook eee eee eee!"
SomeClass.speak = speak

वह कोड क्लास पर बोलने नामक एक विधि को अधिलेखित / निर्मित करेगा। जेफ एटवुड में हाल ही में बंदर के पेटिंग पर पोस्ट । वह C # 3.0 में एक उदाहरण दिखाता है जो वर्तमान भाषा है जो मैं काम के लिए उपयोग करता हूं।


6
लेकिन यह केवल एक ही नहीं, वर्ग के सभी उदाहरणों को प्रभावित करता है।
ग्लॉगल

14

एक विधि को उदाहरण के लिए बाँधने के लिए आप लैम्ब्डा का उपयोग कर सकते हैं:

def run(self):
    print self._instanceString

class A(object):
    def __init__(self):
        self._instanceString = "This is instance string"

a = A()
a.run = lambda: run(a)
a.run()

आउटपुट:

This is instance string

9

बिना किसी इंस्टेंस के एक विधि को संलग्न करने के लिए कम से कम दो तरीके हैं types.MethodType:

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>> 
>>> def foo(firstargument):
...  print 'im foo, invoked with: ', firstargument

>>> foo
<function foo at 0x978548c>

1:

>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>

2:

>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>

उपयोगी लिंक:
डेटा मॉडल - डिस्क्रिप्टर
डिस्क्रिप्टर हॉवेटो गाइड - इनवॉइसिंग डिस्क्रिप्टर


7

आप जिस चीज की तलाश कर रहे हैं, setattrमुझे विश्वास है। किसी ऑब्जेक्ट पर विशेषता सेट करने के लिए इसका उपयोग करें।

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>

8
यह वर्ग को थपथपा रहा है A, उदाहरण के लिए नहीं a
एथन फुरमैन

5
वहाँ setattr(A,'printme',printme)बस के बजाय उपयोग करने के लिए एक कारण है A.printme = printme?
टोबियास किन्ज़लर

1
यह समझ में आता है कि क्या कोई रनटाइम के दौरान विधि नाम बनाता है।
rr-

6

चूंकि यह सवाल गैर-पायथन संस्करणों के लिए पूछा गया था, यहाँ जावास्क्रिप्ट है:

a.methodname = function () { console.log("Yay, a new method!") }

5

बाध्यकारी के विभिन्न तरीकों के परिणामों पर एक नज़र के साथ, जेसन प्रैट और समुदाय विकि उत्तरों को समेकित करना:

विशेष रूप से ध्यान दें कि एक वर्ग पद्धति के रूप में बाध्यकारी समारोह को जोड़ने का काम करता है , लेकिन संदर्भित गुंजाइश गलत है।

#!/usr/bin/python -u
import types
import inspect

## dynamically adding methods to a unique instance of a class


# get a list of a class's method type attributes
def listattr(c):
    for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
        print m[0], m[1]

# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
    c.__dict__[name] = types.MethodType(method, c)

class C():
    r = 10 # class attribute variable to test bound scope

    def __init__(self):
        pass

    #internally bind a function as a method of self's class -- note that this one has issues!
    def addmethod(self, method, name):
        self.__dict__[name] = types.MethodType( method, self.__class__ )

    # predfined function to compare with
    def f0(self, x):
        print 'f0\tx = %d\tr = %d' % ( x, self.r)

a = C() # created before modified instnace
b = C() # modified instnace


def f1(self, x): # bind internally
    print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
    print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
    print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
    print 'f4\tx = %d\tr = %d' % ( x, self.r )


b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')


b.f0(0) # OUT: f0   x = 0   r = 10
b.f1(1) # OUT: f1   x = 1   r = 10
b.f2(2) # OUT: f2   x = 2   r = 10
b.f3(3) # OUT: f3   x = 3   r = 10
b.f4(4) # OUT: f4   x = 4   r = 10


k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

b.f0(0) # OUT: f0   x = 0   r = 2
b.f1(1) # OUT: f1   x = 1   r = 10  !!!!!!!!!
b.f2(2) # OUT: f2   x = 2   r = 2
b.f3(3) # OUT: f3   x = 3   r = 2
b.f4(4) # OUT: f4   x = 4   r = 2

c = C() # created after modifying instance

# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>

print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>

print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>

व्यक्तिगत रूप से, मैं बाहरी ADDMETHOD फ़ंक्शन मार्ग को पसंद करता हूं, क्योंकि यह मुझे इट्रेटर के साथ-साथ गतिशील रूप से नए तरीके के नाम प्रदान करने की अनुमति देता है।

def y(self, x):
    pass
d = C()
for i in range(1,5):
    ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>

addmethodनिम्नलिखित तरीके से लिखित def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )समस्या हल करती है
एंटनी हैचकिंस

5

यह वास्तव में "जेसन प्रैट" के उत्तर के लिए एक ऐडऑन है

हालांकि जैसन जवाब देता है, यह केवल तभी काम करता है जब कोई वर्ग में कोई फ़ंक्शन जोड़ना चाहता है। मेरे लिए यह तब काम नहीं किया जब मैंने .py स्रोत कोड फ़ाइल से पहले से मौजूद पद्धति को फिर से लोड करने का प्रयास किया।

मुझे वर्कअराउंड खोजने में उम्र लग गई, लेकिन चाल आसान लगती है ... 1. स्रोत कोड फ़ाइल से कोड आयात करें। 2. फिर से लोड करने के लिए मजबूर करें। 3. प्रकार का उपयोग करें। FunctionType (...) कन्वर्ट करने के लिए एक फ़ंक्शन में आयातित और बाध्य विधि आप वर्तमान वैश्विक चर पर भी पास कर सकते हैं, क्योंकि पुनः लोड की गई विधि एक अलग नामस्थान 4. में होगी। अब आप "जेसन प्रैट" द्वारा सुझाए गए प्रकारों का उपयोग करके जारी रख सकते हैं। MethodType (... )

उदाहरण:

# this class resides inside ReloadCodeDemo.py
class A:
    def bar( self ):
        print "bar1"

    def reloadCode(self, methodName):
        ''' use this function to reload any function of class A'''
        import types
        import ReloadCodeDemo as ReloadMod # import the code as module
        reload (ReloadMod) # force a reload of the module
        myM = getattr(ReloadMod.A,methodName) #get reloaded Method
        myTempFunc = types.FunctionType(# convert the method to a simple function
                                myM.im_func.func_code, #the methods code
                                globals(), # globals to use
                                argdefs=myM.im_func.func_defaults # default values for variables if any
                                ) 
        myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
        setattr(self,methodName,myNewM) # add the method to the function

if __name__ == '__main__':
    a = A()
    a.bar()
    # now change your code and save the file
    a.reloadCode('bar') # reloads the file
    a.bar() # now executes the reloaded code

3

अगर यह किसी भी मदद के लिए हो सकता है, तो मैंने हाल ही में गोरिल्ला नामक पायथन लाइब्रेरी को जारी किया है ताकि बंदर के पेटिंग की प्रक्रिया को और अधिक सुविधाजनक बनाया जा सके।

needle()एक मॉड्यूल का उपयोग करने के लिए एक फ़ंक्शन का उपयोग करके नाम guineapigइस प्रकार है:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")

लेकिन यह अधिक दिलचस्प उपयोग के मामलों का भी ध्यान रखता है जैसा कि प्रलेखन से एफएक्यू में दिखाया गया है ।

कोड GitHub पर उपलब्ध है


3

यह प्रश्न वर्षों पहले खोला गया था, लेकिन हे, डेकोरेटरों का उपयोग करके एक फ़ंक्शन के बंधन को क्लास इंस्टेंस पर अनुकरण करने का एक आसान तरीका है:

def binder (function, instance):
  copy_of_function = type (function) (function.func_code, {})
  copy_of_function.__bind_to__ = instance
  def bound_function (*args, **kwargs):
    return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
  return bound_function


class SupaClass (object):
  def __init__ (self):
    self.supaAttribute = 42


def new_method (self):
  print self.supaAttribute


supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)

otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)

otherInstance.supMethod ()
supaInstance.supMethod ()

वहां, जब आप बाइंडर डेकोरेटर को फंक्शन और इंस्टेंस पास करते हैं, तो यह एक नया फंक्शन बनाएगा, जिसमें पहले वाले कोड ऑब्जेक्ट होंगे। फिर, वर्ग के दिए गए उदाहरण को नए बनाए गए फ़ंक्शन की विशेषता में संग्रहीत किया जाता है। डेकोरेटर एक (तीसरे) फ़ंक्शन को स्वचालित रूप से कॉपी किए गए फ़ंक्शन को कॉल करता है, उदाहरण के लिए पहला पैरामीटर।

निष्कर्ष में, आप एक फ़ंक्शन का अनुकरण कर रहे हैं जो वर्ग उदाहरण के लिए बाध्यकारी है। मूल फ़ंक्शन को अपरिवर्तित करने दें।


2

जेसन प्रैट ने जो पोस्ट किया है वह सही है।

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

जैसा कि आप देख सकते हैं, पायथन बी () a (a) से भिन्न नहीं मानता है। पायथन में सभी विधियां केवल चर हैं जो फ़ंक्शन होती हैं।


7
आप वर्ग को पैच कर रहे हैं Test, इसका कोई उदाहरण नहीं।
ईथन फुरमैन

आप एक वर्ग में विधि जोड़ रहे हैं, वस्तु उदाहरण नहीं।
टॉमसॉयर

2

मुझे यह अजीब लगता है कि किसी ने भी उल्लेख नहीं किया है कि ऊपर सूचीबद्ध सभी तरीके जोड़े गए विधि और उदाहरण के बीच एक चक्र संदर्भ बनाता है, जिससे ऑब्जेक्ट कचरा संग्रहण तक लगातार बना रहता है। ऑब्जेक्ट के वर्ग का विस्तार करके एक डिस्क्रिप्टर को जोड़ने की एक पुरानी चाल थी:

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.