बंदर का पेटिंग क्या है?


547

मैं यह समझने की कोशिश कर रहा हूं कि बंदर का पेटिंग या बंदर का पैच क्या है?

क्या कुछ ऐसा है जैसे कि तरीके / ऑपरेटर ओवरलोडिंग या डेलिगेटिंग हैं?

क्या यह इन चीजों के साथ कुछ सामान्य है?

जवाबों:


522

नहीं, यह उन चीजों में से किसी की तरह नहीं है। यह बस रनटाइम पर विशेषताओं का गतिशील प्रतिस्थापन है।

उदाहरण के लिए, एक विधि पर विचार करें get_data। यह विधि एक बाहरी लुकअप (डेटाबेस या वेब एपीआई पर, उदाहरण के लिए) करती है, और कक्षा में विभिन्न अन्य तरीके इसे कहते हैं। हालाँकि, एक इकाई परीक्षण में, आप बाहरी डेटा स्रोत पर निर्भर नहीं होना चाहते हैं - इसलिए आप गतिशील रूप से get_dataस्टब के साथ विधि को प्रतिस्थापित करते हैं जो कुछ निश्चित डेटा लौटाता है।

क्योंकि पायथन कक्षाएं उत्परिवर्तनीय हैं, और विधियां केवल कक्षा की विशेषता हैं, आप इसे जितना चाहें उतना कर सकते हैं - और, वास्तव में, आप एक मॉड्यूल में कक्षाओं और कार्यों को बिल्कुल उसी तरह से बदल सकते हैं।

लेकिन, एक टिप्पणीकार ने कहा, जब बंदर पकड़ने पर सावधानी बरतें:

  1. अगर आपके टेस्ट लॉजिक के अलावा कुछ और भी कॉल get_dataकरता है, तो यह आपके बन्दर-पैच वाले रिप्लेसमेंट को ऑरिजिनल की बजाय बुलाएगा - जो अच्छा या बुरा हो सकता है। बस खबरदार।

  2. यदि कुछ चर या विशेषता मौजूद है, जो get_dataउस समय तक आपके द्वारा प्रतिस्थापित किए जाने वाले फ़ंक्शन को भी इंगित करता है, तो यह उपनाम अपना अर्थ नहीं बदलेगा और मूल को इंगित करता रहेगा get_data। (क्यों? पायथन सिर्फ get_dataआपकी कक्षा में नाम को किसी अन्य फ़ंक्शन ऑब्जेक्ट के लिए पुन: डिज़ाइन करता है; अन्य नाम बाइंडिंग सभी से प्रभावित नहीं होते हैं।)


1
@LutzPrechelt सिर्फ मेरे लिए स्पष्ट होना, आपका क्या मतलब है pointing to the original get_data function? क्या आपका मतलब है जब आप एक चर के अंदर एक फ़ंक्शन को स्टोर करते हैं यदि कोई व्यक्ति उस फ़ंक्शन को बदलता है जो चर पुराने को इंगित करता रहेगा?
फैब्रिकोरिसिट्टो

3
@fabriciorissetto: आप आमतौर पर पायथन में फ़ंक्शन ऑब्जेक्ट नहीं बदलते हैं। जब आप बंदर-पैच करते हैं get_data, तो आप नाम get_dataको मॉक फ़ंक्शन के लिए रिबंड करते हैं । यदि कार्यक्रम में कहीं और कोई दूसरा नाम फ़ंक्शन-फ़ंक्शन-पूर्व-ज्ञात-जैसे से बंधा हुआ है get_data, तो उस अन्य नाम के लिए कुछ भी नहीं बदलेगा।
लुत्ज प्रेचल

1
@LutzPrechelt क्या आप इस पर कुछ और बता सकते हैं?
केल्विन कू

मुझे लगता है कि बंदर पैचिंग विशेष रूप से डिबगिंग के लिए, और सज्जाकार या वस्तु कारखानों के कार्यों में उपयोगी हो सकती है। हालांकि, याद रखें कि निहितार्थ से बेहतर है इसलिए सुनिश्चित करें कि आपका कोड संदर्भ-असंवेदनशील है, "गोटो को हानिकारक माना जाता है", आदि पढ़ें ...
aoeu256

तो, यह 'eval' फ़ंक्शन का उपयोग करने के लिए सिर्फ कुछ है, जहां आप रनटाइम पर नया कोड डाल सकते हैं?
wintermute

363

मंकीपॉच पायथन कोड का एक टुकड़ा है जो रनटाइम पर (आमतौर पर स्टार्टअप में) अन्य कोड को बढ़ाता या संशोधित करता है।

एक साधारण उदाहरण इस तरह दिखता है:

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return "ook ook eee eee eee!"

SomeClass.speak = speak

स्रोत: Zope विकी पर MonkeyPatch पेज।


126

एक बंदर पैच क्या है?

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

उपयोग में उदाहरण

पंडों के दस्तावेज में बंदर-पेटिंग का एक उदाहरण है:

import pandas as pd
def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

इसे तोड़ने के लिए, पहले हम अपने मॉड्यूल को आयात करते हैं:

import pandas as pd

अगला हम एक विधि परिभाषा बनाते हैं, जो किसी भी वर्ग परिभाषाओं के दायरे से बाहर और बाहर मौजूद है (क्योंकि एक फ़ंक्शन और एक अनबाउंड विधि के बीच अंतर काफी अर्थहीन है, पायथन 3 अनबाउंड विधि से दूर हो जाता है):

def just_foo_cols(self):
    """Get a list of column names containing the string 'foo'

    """
    return [x for x in self.columns if 'foo' in x]

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

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class

और फिर हम कक्षा के एक उदाहरण पर विधि का उपयोग कर सकते हैं, और जब हम कर सकते हैं तब विधि को हटा दें:

df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method

नाम-जनन के लिए कैविएट

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

परीक्षण उदाहरण

हम इस ज्ञान का उपयोग कैसे कर सकते हैं, उदाहरण के लिए, परीक्षण में?

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

import datasource

def get_data(self):
    '''monkey patch datasource.Structure with this to simulate error'''
    raise datasource.DataRetrievalError

datasource.Structure.get_data = get_data

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

बस ऊपर Structureकरने से प्रक्रिया के जीवन के लिए वस्तु बदल जाएगी , इसलिए आप ऐसा करने से बचने के लिए अपने unittests में setups और taddowns का उपयोग करना चाहते हैं:

def setUp(self):
    # retain a pointer to the actual real method:
    self.real_get_data = datasource.Structure.get_data
    # monkey patch it:
    datasource.Structure.get_data = get_data

def tearDown(self):
    # give the real method back to the Structure object:
    datasource.Structure.get_data = self.real_get_data

(जबकि इसके बाद के संस्करण ठीक है, यह शायद एक बेहतर विचार हो उपयोग करने के लिए होगा mockकोड पैच करने के लिए पुस्तकालय। mockके patchडेकोरेटर कम त्रुटि ऊपर कर से होने का खतरा है, जो कोड के अधिक लाइनों और इस प्रकार अधिक त्रुटियों को पेश करने के अवसर की आवश्यकता होगी होगा । मुझे अभी तक इस कोड की समीक्षा करनी है mockलेकिन मुझे लगता है कि यह इसी तरह से बंदर-पैचिंग का उपयोग करता है।)


तो क्या वास्तविक विधि के संदर्भ को संग्रहीत करने के लिए मंकीपैचर पर बोझ है? उदाहरण के लिए, यदि कोई "पॉइंटर को बनाए रखना" कदम भूल जाता है, तो वह खो जाता है?
टॉमी

3
@ टॉमी यदि "अधिलेखित" पद्धति को फिर से शून्य पर जाता है - यह कचरा एकत्र किया जाता है, और इस प्रकार प्रक्रिया के जीवन के लिए "खो" जाता है (या जब तक कि यह जिस मॉड्यूल में उत्पन्न नहीं होता है उसे फिर से लोड किया जाता है, लेकिन यह आमतौर पर अस्वीकृत है)।
हारून हॉल

33

विकिपीडिया के अनुसार :

पायथन में, बन्दर पैच शब्द केवल रनटाइम पर एक वर्ग या मॉड्यूल के गतिशील संशोधनों को संदर्भित करता है, मौजूदा थर्ड-पार्टी कोड को बग या सुविधा के लिए वर्कअराउंड के रूप में पैच करने के इरादे से प्रेरित होता है जो आपकी इच्छा के अनुसार कार्य नहीं करता है।


16

पहला: मंकी पैचिंग एक दुष्ट हैक (मेरी राय में) है।

यह अक्सर कस्टम कार्यान्वयन के साथ मॉड्यूल या वर्ग स्तर पर एक विधि को बदलने के लिए उपयोग किया जाता है।

जब आप मूल कोड को प्रतिस्थापित नहीं कर सकते, तो सबसे आम usecase एक बग या मॉड्यूल या वर्ग में बग के लिए समाधान जोड़ रहा है। इस मामले में आप "गलत" कोड को अपने स्वयं के मॉड्यूल / पैकेज के अंदर कार्यान्वयन के साथ बंदर पैचिंग के माध्यम से बदलते हैं।


8
मामले में कुछ मॉड्यूल एक ही बात बंदर-पैचिंग: आप बर्बाद कर रहे हैं।
एंड्रियास जुंग

49
हालांकि इसकी शक्ति वास्तव में खतरनाक हो सकती है, यह परीक्षण के लिए महान है
dkrikun

1
सबसे आम usecase वास्तव में परीक्षण के लिए है, विशेष रूप से इकाई परीक्षण। आप केवल अपने कोड का परीक्षण करना चाहते हैं, इसलिए आप अपेक्षित परिणाम वापस करने के लिए किसी भी बाहरी कॉल को पैच करते हैं।
ब्रोकोली

1
यह बुरा नहीं है, मैं इसे अन्य लोगों के सॉफ़्टवेयर में बग्स को पैच करने के लिए उपयोग करता हूं, जब तक कि एक नई रिलीज़ के लिए फोर्किंग और एक नई निर्भरता बनाने के बजाय बाहर नहीं निकलता।
नुरेटिन

1
बंदर पैचिंग को "शुद्ध कार्यात्मक तरीके से" किया जा सकता है न कि परस्पर, "संदर्भ-संवेदनशील", गोटो जैसा तरीका केवल डेकोरेटर के अंदर पैचिंग करके जो आपके वर्ग / विधि के नए पैच किए गए संस्करण को वापस करता है (बजाय इसे संशोधित करने के)। बहुत सारे C # / Java प्रोग्रामर REPL संचालित विकास के बारे में नहीं जानते हैं, इसलिए वे अपने IDEs में कोड रखते हैं जिसके लिए यह आवश्यक है कि सब कुछ निर्धारित हो। चूंकि C # / Java में मंकी-पैचिंग नहीं थी, इसलिए जब वे इसे जावास्क्रिप्ट, स्मॉलटॉक, लिस्प, पायथन, आदि में देखते हैं, तो इसकी बुराई मान लेते हैं ... क्योंकि यह उनके स्थिर आईडीई संचालित विकास अभ्यास के खिलाफ है।
aoeu256

13

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

इसे बुरा माना जाता है क्योंकि इसका मतलब है कि किसी वस्तु की परिभाषा पूरी तरह से या सटीक वर्णन नहीं करती है कि यह वास्तव में कैसे व्यवहार करती है।


हालांकि, बंदर पैचिंग तब तक उपयोगी हो सकती है जब तक कि किसी मौजूदा ऑब्जेक्ट या क्लास को संशोधित करने के बजाय, आप किसी डेकोरेटर के अंदर सदस्यों के साथ ऑब्जेक्ट के नए संस्करण का निर्माण करते हैं जो चिल्लाता है "अरे आई एम पैचिंग टू यू पैच"।
aoeu256

आप पैच किए गए सदस्य पर एनोटेशन का उपयोग उस पैच किए गए सदस्य में संग्रहीत करने के लिए कर सकते हैं, जिसे पैच में पैच करने के लिए डेकोरेटर का उपयोग किया गया था। कहते हैं कि आपके पास एक पूर्ववत डेकोरेटर है जो एक पूर्ववत विधि के साथ फ़ंक्शन ऑब्जेक्ट का एक नया पूर्ववत संस्करण बनाता है। आप अपने पूर्ववत डेकोरेटर की ओर इशारा करते हुए डेकोरेटर को पैचर फील्ड में रख सकते हैं।
aoeu256

5

मंकी पैचिंग रनटाइम के दौरान क्लास में मौजूदा क्लासेस या तरीकों को फिर से खोल रही है और व्यवहार को बदल रही है, जिसे सावधानी से इस्तेमाल किया जाना चाहिए, या आपको इसका उपयोग केवल तब करना चाहिए जब आपको वास्तव में ज़रूरत हो।

जैसा कि पायथन एक गतिशील प्रोग्रामिंग भाषा है, कक्षाएं परस्पर भिन्न होती हैं ताकि आप उन्हें फिर से खोल सकें और संशोधित कर सकें या उन्हें बदल भी सकें।


1

बंदर का पेटिंग क्या है?मंकी पैचिंग एक ऐसी तकनीक है जिसका इस्तेमाल रन-टाइम में कोड के एक टुकड़े के व्यवहार को गतिशील रूप से अपडेट करने के लिए किया जाता है।

बन्दर पैचिंग का उपयोग क्यों करें?यह हमें स्रोत कोड को संशोधित किए बिना रनटाइम पर पुस्तकालयों, मॉड्यूल, वर्गों या विधियों के व्यवहार को संशोधित करने या विस्तारित करने की अनुमति देता है

निष्कर्ष बंदर पैचिंग एक शांत तकनीक है और अब हमने सीखा है कि पायथन में ऐसा कैसे किया जाए। हालांकि, जैसा कि हमने चर्चा की, इसकी अपनी कमियां हैं और इसका सावधानी से उपयोग किया जाना चाहिए।

अधिक जानकारी के लिए कृपया देखें [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

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