Cos'è il patching delle scimmie?


549

Sto cercando di capire, cos'è il patch di scimmia o un patch di scimmia?

È qualcosa come metodi / operatori che sovraccaricano o delegano?

Ha qualcosa in comune con queste cose?


Penso che la definizione di Google sia utile e più generale: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

Risposte:


522

No, non è come nessuna di quelle cose. È semplicemente la sostituzione dinamica degli attributi in fase di esecuzione.

Ad esempio, considera una classe che ha un metodo get_data. Questo metodo esegue una ricerca esterna (su un database o un'API Web, ad esempio), e vari altri metodi nella classe lo chiamano. Tuttavia, in un unit test, non si desidera dipendere dall'origine dati esterna, quindi sostituire in modo dinamico il get_datametodo con uno stub che restituisce alcuni dati fissi.

Poiché le classi Python sono mutabili e i metodi sono solo attributi della classe, puoi farlo come preferisci e, in effetti, puoi persino sostituire classi e funzioni in un modulo esattamente nello stesso modo.

Ma, come ha sottolineato un commentatore , fai attenzione quando monkeypatching:

  1. Se qualcos'altro oltre alla tua logica di test chiama get_dataanche, chiamerà anche la tua sostituzione con patch scimmia piuttosto che l'originale - che può essere buona o cattiva. Attenzione.

  2. Se esiste qualche variabile o attributo che punta anche alla get_datafunzione al momento della sostituzione, questo alias non cambierà il suo significato e continuerà a puntare all'originale get_data. (Perché? Python abbina semplicemente il nome get_datanella tua classe a qualche altro oggetto funzione; gli altri collegamenti ai nomi non hanno alcun impatto.)


1
@LutzPrechelt solo per essere chiari per me, con cosa intendi pointing to the original get_data function? Intendi quando memorizzi una funzione all'interno di una variabile se qualcuno cambia quella funzione continuerà a puntare a quella vecchia?
fabriciorissetto,

3
@fabriciorissetto: normalmente non si modificano gli oggetti funzione in Python. Quando si applica la patch di scimmia get_data, si associa il nome get_dataa una funzione simulata. Se un altro nome da qualche altra parte nel programma è associato alla funzione precedentemente nota come as get_data, nulla cambierà per quell'altro nome.
Lutz Prechelt,

1
@LutzPrechelt Potresti spiegare qualcosa in più su questo?
Calvin Ku,

Penso che il patching delle scimmie possa essere utile soprattutto per il debug e nelle funzioni di decoratori o fabbriche di oggetti. Tuttavia, ricorda che esplicito è meglio di implicito, quindi assicurati che il tuo codice sia insensibile al contesto, leggi "Vai a considerato dannoso", ecc ...
aoeu256

Quindi, è solo qualcosa di simile all'utilizzo della funzione 'eval', in cui è possibile inserire un nuovo codice in fase di esecuzione?
Wintermute

363

Un MonkeyPatch è un pezzo di codice Python che estende o modifica altro codice in fase di esecuzione (in genere all'avvio).

Un semplice esempio è simile al seguente:

from SomeOtherProduct.SomeModule import SomeClass

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

SomeClass.speak = speak

Fonte: pagina MonkeyPatch sul wiki di Zope.


126

Cos'è una patch scimmia?

In poche parole, il patching delle scimmie sta apportando modifiche a un modulo o a una classe mentre il programma è in esecuzione.

Esempio in uso

C'è un esempio di patching delle scimmie nella documentazione di 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

Per scomporlo, prima di tutto importiamo il nostro modulo:

import pandas as pd

Quindi creiamo una definizione di metodo, che esiste senza restrizioni e libera al di fuori dell'ambito di qualsiasi definizione di classe (poiché la distinzione è abbastanza insignificante tra una funzione e un metodo non associato, Python 3 elimina il metodo non associato):

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]

Quindi colleghiamo semplicemente quel metodo alla classe su cui vogliamo usarlo:

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

Quindi possiamo usare il metodo su un'istanza della classe ed eliminare il metodo quando abbiamo finito:

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

Avvertenza per l'uccisione del nome

Se stai usando il name-mangling (prefisso gli attributi con un doppio trattino basso, che altera il nome e che non ti consiglio) dovrai farlo manualmente. Dal momento che non raccomando di cambiare nome, non lo dimostrerò qui.

Esempio di test

Come possiamo usare questa conoscenza, ad esempio, nei test?

Supponiamo di dover simulare una chiamata di recupero dati a un'origine dati esterna che si traduca in un errore, perché in tal caso vogliamo garantire un comportamento corretto. Siamo in grado di applicare una patch alla struttura dei dati per garantire questo comportamento. (Quindi usando un nome di metodo simile a quello suggerito da 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

E quando lo testiamo per un comportamento che si basa su questo metodo generando un errore, se implementato correttamente, otterremo quel comportamento nei risultati del test.

Basta fare quanto sopra per modificare l' Structureoggetto per la durata del processo, quindi ti consigliamo di utilizzare impostazioni e smontaggi nei tuoi unittest per evitare di farlo, ad esempio:

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

(Mentre quanto sopra va bene, probabilmente sarebbe una migliore idea usare la mocklibreria per patchare il codice. mockIl patchdecoratore sarebbe meno soggetto ad errori rispetto a quanto sopra, il che richiederebbe più righe di codice e quindi maggiori opportunità di introdurre errori Devo ancora rivedere il codice mockma immagino che usi il patching delle scimmie in un modo simile.)


così è l'onere per il monkeypatcher memorizzare un riferimento al metodo reale? ad esempio, cosa succede se si dimentica il passaggio "conserva un puntatore", si perde?
Tommy,

3
@ Tommy Se il conto alla rovescia di un metodo "sovrascritto" va a zero - si tratta di immondizia raccolta, e quindi "persa" per la durata del processo (o a meno che il modulo da cui è stato originato non sia ricaricato, ma di solito è sconsigliato).
Aaron Hall

33

Secondo Wikipedia :

In Python, il termine scimmia patch si riferisce solo a modifiche dinamiche di una classe o di un modulo in fase di runtime, motivati ​​dall'intento di correggere codice di terze parti esistente come soluzione alternativa a un bug o a una funzionalità che non agisce come desiderato.


16

Primo: il patching delle scimmie è un trucco malvagio (secondo me).

Viene spesso utilizzato per sostituire un metodo a livello di modulo o di classe con un'implementazione personalizzata.

L'usecase più comune è l'aggiunta di una soluzione alternativa per un bug in un modulo o in una classe quando non è possibile sostituire il codice originale. In questo caso si sostituisce il codice "errato" tramite patch scimmia con un'implementazione all'interno del proprio modulo / pacchetto.


8
Nel caso in cui alcuni moduli rompano le scimmie la stessa cosa: sei condannato.
Andreas Jung,

49
Mentre il suo potere potrebbe davvero essere pericoloso in generale, è ottimo per i test
dkrikun

1
Il caso d'uso più comune è in realtà per i test, in particolare i test unitari. Vuoi testare solo il tuo codice, quindi correggi qualsiasi chiamata esterna per restituire un risultato previsto.
brocoli,

1
non è malvagio, lo uso per correggere bug nel software di altre persone fino a quando non esce una nuova versione invece di biforcarsi e creare una nuova dipendenza.
Nurettin,

1
Il patching delle scimmie può essere fatto in un "modo funzionale puro", non nel modo mutabile, "sensibile al contesto", simile a un goto, facendo solo patching all'interno dei decoratori che restituiscono una nuova versione patchata della tua classe / metodo (piuttosto che modificarla). Molti programmatori C # / Java non conoscono lo sviluppo guidato da REPL, quindi codificano nei loro IDE che richiedono che tutto sia definito staticamente. Dal momento che C # / Java non avevano il patching delle scimmie, assumono il loro male quando lo vedono in JavaScript, Smalltalk, Lisp, Python, ecc ... mentre vanno contro la loro pratica di sviluppo guidata dall'IDE statica.
aoeu256,

13

Il patching delle scimmie può essere eseguito solo in linguaggi dinamici, di cui python è un buon esempio. La modifica di un metodo in fase di esecuzione anziché l'aggiornamento della definizione dell'oggetto è un esempio; allo stesso modo, l'aggiunta di attributi (sia metodi che variabili) in fase di esecuzione è considerata patch scimmia. Ciò avviene spesso quando si lavora con moduli per i quali non si ha l'origine, in modo tale che le definizioni degli oggetti non possano essere facilmente modificate.

Ciò è considerato negativo perché significa che la definizione di un oggetto non descrive in modo completo o accurato come si comporta effettivamente.


Tuttavia, il patching delle scimmie può essere utile fintanto che invece di modificare un oggetto o una classe esistente, crei una nuova versione di un oggetto con patch nei membri all'interno di un decoratore che grida "hey im andando a patch te".
aoeu256,

È possibile utilizzare le annotazioni sui membri con patch per memorizzare nel membro con patch quale decoratore è stato utilizzato per applicare patch nelle patch. Supponiamo che tu abbia un decoratore annullabile che crea una nuova versione annullabile di un oggetto funzione con un metodo di annullamento. Puoi inserire nel decoratore un campo patcher che punta al tuo decoratore inevitabile.
aoeu256,

5

Il patching delle scimmie sta riaprendo le classi o i metodi esistenti in classe in fase di runtime e sta cambiando il comportamento, che dovrebbe essere usato con cautela, oppure dovresti usarlo solo quando è realmente necessario.

Poiché Python è un linguaggio di programmazione dinamico, le Classi sono mutabili in modo da poterle riaprire e modificare o persino sostituirle.


1

Cos'è il patching delle scimmie? Il patching delle scimmie è una tecnica utilizzata per aggiornare dinamicamente il comportamento di un pezzo di codice in fase di esecuzione.

Perché usare il patching delle scimmie? Ci consente di modificare o estendere il comportamento di librerie, moduli, classi o metodi in fase di runtime senza modificare effettivamente il codice sorgente

Conclusioni Il patching delle scimmie è una tecnica interessante e ora abbiamo imparato a farlo in Python. Tuttavia, come abbiamo discusso, ha i suoi svantaggi e dovrebbe essere usato con attenzione.

Per maggiori informazioni, fare riferimento a [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.