การปะของลิงคืออะไร


548

ฉันกำลังพยายามที่จะเข้าใจการปะของลิงหรือแผ่นปะของลิงคืออะไร

นั่นคือวิธีการ / ผู้ประกอบการมากไปหรือการมอบหมาย?

มันมีอะไรที่เหมือนกันกับสิ่งเหล่านี้หรือไม่?


ฉันคิดว่าคำนิยามจาก google นั้นมีประโยชน์และโดยทั่วไปแล้ว:Monkey patching is a technique to add, modify, or suppress the default behavior of a piece of code at runtime without changing its original source code.
Charlie Parker

คำตอบ:


522

ไม่มันไม่เหมือนสิ่งเหล่านี้เลย มันเป็นเพียงการเปลี่ยนคุณสมบัติแบบไดนามิกที่รันไทม์

get_dataเช่นพิจารณาระดับที่มีวิธีการที่ วิธีนี้ทำการค้นหาภายนอก (บนฐานข้อมูลหรือเว็บ API เป็นต้น) และวิธีการอื่น ๆ ในชั้นเรียนเรียกมันว่า อย่างไรก็ตามในการทดสอบหน่วยคุณไม่ต้องการพึ่งพาแหล่งข้อมูลภายนอก - ดังนั้นคุณจะแทนที่get_dataเมธอดด้วยไดนามิกที่ส่งคืนข้อมูลคงที่บางส่วน

เนื่องจากคลาส Python นั้นไม่แน่นอนและเมธอดเป็นเพียงคุณลักษณะของคลาสคุณจึงสามารถทำสิ่งนี้ได้มากเท่าที่คุณต้องการ - และในความเป็นจริงคุณสามารถแทนที่คลาสและฟังก์ชั่นในโมดูลได้ในลักษณะเดียวกัน

แต่ในฐานะที่เป็นผู้แสดงความคิดเห็นชี้ให้ใช้ความระมัดระวังเมื่อ monkeypatching:

  1. หากมีสิ่งอื่นนอกเหนือจากตรรกะการทดสอบของคุณเรียกget_dataเช่นกันมันก็จะเรียกการแทนที่ลิงที่คุณได้ทำการแก้ไขมากกว่าเดิมซึ่งอาจดีหรือไม่ดีก็ได้ แค่ระวัง

  2. ถ้าตัวแปรบางส่วนหรือแอตทริบิวต์อยู่ที่จุดกับฟังก์ชั่นตามเวลาที่คุณเปลี่ยนสมญานามนี้จะไม่เปลี่ยนความหมายของมันและจะยังคงชี้ไปที่เดิมget_data get_data(เพราะอะไร Python เพียงแค่ rebinds ชื่อget_dataในคลาสของคุณไปยังวัตถุฟังก์ชั่นอื่น ๆ การผูกชื่ออื่น ๆ จะไม่ได้รับผลกระทบเลย)


1
@LutzPrechelt เพียงเพื่อให้ชัดเจนสำหรับฉันคุณหมายถึงpointing to the original get_data functionอะไร? คุณหมายถึงเมื่อคุณเก็บฟังก์ชันไว้ในตัวแปรหรือไม่ถ้ามีคนเปลี่ยนฟังก์ชั่นนั้นตัวแปรจะยังคงชี้ไปที่ฟังก์ชันเก่า
fabriciorissetto

3
@fabriciorissetto: ปกติแล้วคุณจะไม่เปลี่ยนวัตถุฟังก์ชั่นใน Python เมื่อคุณลิงแพทช์get_dataคุณจะใส่ชื่อใหม่get_dataไปยังฟังก์ชันจำลอง หากชื่ออื่นอยู่ที่อื่นในโปรแกรมจะถูกผูกไว้กับ the-function-before-known-as- get_dataจะไม่มีอะไรเปลี่ยนแปลงสำหรับชื่ออื่น
Lutz Prechelt

1
@LutzPrechelt คุณช่วยอธิบายเพิ่มเติมเล็กน้อยได้ไหม?
Calvin Ku

ฉันคิดว่าการปะของลิงนั้นมีประโยชน์อย่างยิ่งสำหรับการดีบั๊กและในการตกแต่งหรือฟังก์ชั่นของโรงงานวัตถุ แต่จำไว้อย่างชัดเจนจะดีกว่าโดยปริยายเพื่อให้แน่ใจว่ารหัสของคุณคือบริบทตายอ่าน "ไปถือว่าอันตราย" ฯลฯ ...
aoeu256

ดังนั้นมันเป็นสิ่งที่คล้ายกับการใช้ฟังก์ชั่น 'eval' ที่คุณสามารถแทรกรหัสใหม่ที่รันไทม์?
wintermute

363

MonkeyPatch เป็นส่วนหนึ่งของรหัส Python ซึ่งขยายหรือแก้ไขรหัสอื่น ๆ ที่รันไทม์ (โดยทั่วไปเมื่อเริ่มต้น)

ตัวอย่างง่ายๆมีลักษณะเช่นนี้:

from SomeOtherProduct.SomeModule import SomeClass

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

SomeClass.speak = speak

ที่มา: หน้าMonkeyPatchบน Zope wiki


126

แพทช์ลิงคืออะไร?

พูดง่ายๆคือการปะแก้ลิงกำลังทำการเปลี่ยนแปลงโมดูลหรือคลาสในขณะที่โปรแกรมกำลังทำงานอยู่

ตัวอย่างการใช้งาน

มีตัวอย่างของการปะแก้ลิงในเอกสาร Pandas:

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

ต่อไปเราจะสร้างนิยามเมธอดซึ่งมีอยู่ไม่ จำกัด และว่างนอกขอบเขตของนิยามคลาสใด ๆ (เนื่องจากความแตกต่างค่อนข้างไม่มีความหมายระหว่างฟังก์ชั่นและเมธอดที่ไม่ถูกผูกไว้

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

Caveat สำหรับชื่อ mangling

หากคุณกำลังใช้ชื่อ -mangling (คุณลักษณะการเติมคำนำหน้าด้วยเครื่องหมายขีดล่างคู่ซึ่งเปลี่ยนชื่อและที่ฉันไม่แนะนำ) คุณจะต้องใช้ชื่อ -mangle ด้วยตนเองหากคุณทำเช่นนี้ เนื่องจากฉันไม่แนะนำชื่อที่เป็นอันตรายฉันจะไม่แสดงที่นี่

ตัวอย่างการทดสอบ

เราจะใช้ความรู้นี้ในการทดสอบได้อย่างไร

สมมติว่าเราต้องการจำลองการเรียกค้นข้อมูลไปยังแหล่งข้อมูลภายนอกที่ทำให้เกิดข้อผิดพลาดเนื่องจากเราต้องการให้แน่ใจว่าพฤติกรรมที่ถูกต้องในกรณีเช่นนี้ เราสามารถแก้ไขโครงสร้างข้อมูลเพื่อให้แน่ใจว่าพฤติกรรมนี้ลิง (ดังนั้นใช้ชื่อวิธีการที่คล้ายกันตามที่แนะนำโดย Daniel Roseman :)

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 ของคุณเพื่อหลีกเลี่ยงการทำเช่น:

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แต่ฉันคิดว่ามันใช้การปะลิงในลักษณะที่คล้ายกัน)


ดังนั้นภาระใน monkeypatcher ในการจัดเก็บการอ้างอิงถึงวิธีการที่แท้จริงคืออะไร? เช่นจะเกิดอะไรขึ้นหากมีใครลืมขั้นตอน "คงตัวชี้" ไว้จะหายไป?
Tommy

3
@ ทอมมี่หาก refcounts เป็นวิธี "เขียนทับ" ไปที่ศูนย์ - มันเป็นขยะที่เก็บรวบรวมและทำให้ "หลงทาง" สำหรับชีวิตของกระบวนการ (หรือถ้าโมดูลนั้นมาถูกโหลดใหม่ แต่มักจะหมดกำลังใจ)
Aaron Hall

33

ตามที่Wikipedia :

ใน Python คำว่า monkey patch อ้างถึงการปรับเปลี่ยนแบบไดนามิกของคลาสหรือโมดูล ณ รันไทม์ซึ่งได้รับแรงบันดาลใจจากเจตนาที่จะแก้ไขโค้ดของบุคคลที่สามที่มีอยู่เพื่อแก้ไขข้อบกพร่องหรือคุณสมบัติที่ไม่ได้ทำตามที่คุณต้องการ


16

อย่างแรก: การปะลิงคือการแฮ็คที่ชั่วร้าย

มันมักจะใช้เพื่อแทนที่วิธีการในโมดูลหรือระดับชั้นด้วยการใช้งานที่กำหนดเอง

usecase ที่พบบ่อยที่สุดคือการเพิ่มวิธีแก้ปัญหาสำหรับข้อบกพร่องในโมดูลหรือคลาสเมื่อคุณไม่สามารถแทนที่รหัสเดิม ในกรณีนี้คุณแทนที่รหัส "ผิด" ผ่านการปะแก้ลิงด้วยการใช้งานภายในโมดูล / แพ็คเกจของคุณเอง


8
ในกรณีที่บางโมดูลการปะของลิงเหมือนกัน: คุณถึงวาระแล้ว
Andreas Jung

49
ในขณะที่พลังของมันอาจเป็นอันตรายโดยทั่วไปมันเป็นเรื่องที่ดีสำหรับการทดสอบ
dkrikun

1
usecase ที่พบบ่อยที่สุดคือการทดสอบโดยเฉพาะอย่างยิ่งการทดสอบหน่วย คุณต้องการทดสอบรหัสของคุณเท่านั้นดังนั้นคุณสามารถแก้ไขการโทรภายนอกเพื่อส่งคืนผลลัพธ์ที่คาดหวัง
brocoli

1
มันไม่ได้เลวร้ายฉันใช้มันเพื่อแก้ไขข้อบกพร่องในซอฟต์แวร์ของคนอื่น ๆ จนกว่าจะมีการเปิดตัวรุ่นใหม่ออกมาแทนที่จะฟอร์กและสร้างการพึ่งพาใหม่
นูเรตต์

1
การปะแก้ของลิงสามารถทำได้ใน "วิธีการทำงานที่บริสุทธิ์" ไม่ใช่วิธีที่ไม่แน่นอน "บริบทที่ไวต่อความรู้สึก" โดยใช้วิธีการปะแก้ภายในนักตกแต่งที่ส่งคืนรุ่น / วิธีการแก้ไขที่ปรับปรุงใหม่ของคุณ (แทนที่จะแก้ไข) โปรแกรมเมอร์ C # / Java จำนวนมากไม่รู้เกี่ยวกับการพัฒนาที่ขับเคลื่อนด้วย REPL ดังนั้นพวกเขาจึงเขียนโค้ดใน IDEs ของพวกเขาที่ต้องการให้ทุกอย่างถูกกำหนดแบบคงที่ เนื่องจาก C # / Java ไม่มีการปะแก้ลิงจึงถือว่าสิ่งชั่วร้ายเมื่อเห็นใน JavaScript, Smalltalk, Lisp, Python และอื่น ๆ ... เนื่องจากขัดกับแนวทางปฏิบัติในการพัฒนา IDE แบบคงที่
aoeu256

13

การปะของลิงสามารถทำได้ในภาษาไดนามิกเท่านั้นซึ่งไพ ธ อนเป็นตัวอย่างที่ดี การเปลี่ยนเมธอดที่รันไทม์แทนการอัพเดตนิยามอ็อบเจ็กต์เป็นหนึ่งในตัวอย่างเช่นเดียวกันการเพิ่มแอ็ตทริบิวต์ (ไม่ว่าเมธอดหรือตัวแปร) ที่รันไทม์จะถูกพิจารณาเป็น patch patch สิ่งเหล่านี้มักเกิดขึ้นเมื่อทำงานกับโมดูลที่คุณไม่มีแหล่งที่มาเช่นคำจำกัดความของวัตถุที่ไม่สามารถเปลี่ยนแปลงได้ง่าย

สิ่งนี้ถือว่าไม่ดีเพราะมันหมายความว่าคำจำกัดความของวัตถุไม่ได้อธิบายอย่างสมบูรณ์หรืออย่างถูกต้องว่ามันทำงานอย่างไร


อย่างไรก็ตามการปะของลิงจะมีประโยชน์ได้ตราบใดที่แทนที่จะแก้ไขวัตถุหรือคลาสที่มีอยู่แล้วคุณสร้างวัตถุใหม่ที่มีการปะแก้ในสมาชิกภายในมัณฑนากรซึ่งกรีดร้องว่า
aoeu256

คุณสามารถใช้คำอธิบายประกอบกับสมาชิกที่ได้รับการแก้ไขเพื่อจัดเก็บในสมาชิกที่ได้รับการแก้ไขซึ่งมัณฑนากรที่ใช้ในการแก้ไขในแพทช์ ให้บอกว่าคุณมี decorator ที่เลิกทำการได้ที่สร้างฟังก์ชั่นวัตถุที่เลิกทำการใหม่ด้วยวิธีเลิกทำ คุณสามารถใส่มัณฑนากรลงในฟิลด์ตัวแก้ไขที่ชี้ไปที่มัณฑนากรที่เปิดไม่ได้ของคุณ
aoeu256

5

การปะของลิงกำลังเปิดคลาสหรือวิธีการที่มีอยู่ในคลาสอีกครั้งในขณะรันไทม์และเปลี่ยนพฤติกรรมซึ่งควรใช้ด้วยความระมัดระวังหรือคุณควรใช้เฉพาะเมื่อคุณต้องการจริงๆ

เนื่องจาก Python เป็นภาษาการเขียนโปรแกรมแบบไดนามิกคลาสจึงไม่แน่นอนดังนั้นคุณสามารถเปิดอีกครั้งและแก้ไขหรือแทนที่ได้


1

การปะของลิงคืออะไร Monkey patching เป็นเทคนิคที่ใช้ในการอัพเดทพฤติกรรมของโค้ดในช่วงรันไทม์

ทำไมต้องใช้การปะของลิง มันช่วยให้เราสามารถปรับเปลี่ยนหรือขยายพฤติกรรมของห้องสมุดโมดูลคลาสหรือวิธีการที่รันไทม์โดยไม่ต้องแก้ไขซอร์สโค้ดจริง

สรุปการปะลิงเป็นเทคนิคที่ยอดเยี่ยมและตอนนี้เราได้เรียนรู้วิธีการทำใน Python อย่างไรก็ตามอย่างที่เราพูดถึงมันมีข้อเสียของตัวเองและควรใช้อย่างระมัดระวัง

สำหรับข้อมูลเพิ่มเติมโปรดอ้างอิง [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.