Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Monkeypatching : demande d' explications

+5 votes

Je ne suis pas certain d' avoir vraiment compris ce qu'est le monkeypatching.
Je crois que ça sert à ajouter de nouvelles méthodes à une classes existante que l'on importe depuis un module. Et pour les attributs ? Quelle différence par rapport aux décorateurs ? et les créateurs de décorateurs à la volée ?

demandé 27-Mai-2015 par buffalo974 (2,886 points)
reclassée 27-Mai-2015 par buffalo974

lol, ça fait beaucoup de questions à la fois :x

J'ai édité ma réponse pour répondre aux sous-questions que t'as édité ;)

2 Réponses

+6 votes
 
Meilleure réponse

Je ne suis pas certain d' avoir vraiment compris ce qu'est le
monkeypatching.

En gros (très gros) :P

Un MonkeyPatch c'est un morceau de code (pour nous ce sera du Python) qui prolonge ou modifie un autre code lors de l'exécution ( généralement au démarrage ) .

Le fait de faire du MonkeyPatching signifie le fait d'écrire ou d'exécuter un monkeypatch.

Un exemple tout con ressemble à ce truc :

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return ("Je suis en train de monkeypatcher, ça se voit pas??")

SomeClass.speak = speak
  • Dans ce petit exemple, Si SomeClass n'a pas déjà une méthode nommée
    speak, à partir de maintenant ce sera le cas ;) ,c'est à dire que
    SomeClass aura dorénavant une méthode appelée speak.

  • Tandis que si une méthode nommée speak existe déjà, sa définition
    sera écrasée
    par notre nouveau code.

Sources/Lectures : ici et ou encore par là.

Edit :

Et pour les attributs ?

On peut aussi ajouter ou modifier les attributs d'une classe, exemple :

class SomeClass(object):

    def __init__(self):
        """Constructor"""
        pass

# Ajout d'un nouvel attribut
>>> SomeClass.attr="a"
>>> SomeClass.attr
'a' 
# Modification de l'attribut courant
>>> SomeClass.attr="b"
>>> SomeClass.attr
'b'

Quelle différence par rapport aux décorateurs?

On peut balancer par exemple que le monkey patch garde l'info du code patché (comme la doc ou encore la liste des arguments *args ou kwargs), il se contente de l'étendre ou le modifier, tandis que le **décorateur non(sans utiliser de modules spécifiques qui rendent ça possible), comme le dit si bien l'article de S&M :

Mais quand vous décorez une fonction, vous l’enrobez dans une autre,
détruisant ces informations:

Pour reprendre l'exemple de l'article (avec quelques modifs):

#avant d'utiliser le décorateur

def ma_fonction():
    """
        C'est une super fonction
    """
    pass
ma_fonction.__doc__ "\n            C'est une super fonction\n        "

#en utilisant le décorateur

def decorateur_inutile(func):
    def wrapper():
        func()
    return wrapper

@decorateur_inutile
def ma_fonction():
    """
        C'est une super fonction
    """
    pass

>>> print ma_fonction.__doc__
None

et les créateurs de décorateurs à la volée ?

Pas besoin de répondre à cette question vu la réponse donnée ci-dessus ;)

Ps : Tu devrais ouvrir un post par question, pour qu'on puisse réfléchir sur tes questions unes à unes, car balancer (au moins) 5 questions d'un coup, ça fait beaucoup et on perd en lisibilité, notamment lorsque quelqu'un cherche une info précise, il risque de s'embrouiller en passant par là =s.

Cordialement...;p

répondu 27-Mai-2015 par boblinux (3,092 points)
sélectionné 28-Mai-2015 par buffalo974

c'est fait, j'ai éclaté la question en 2 ^^

En fait le MonkeyPatching c'est un peu comme de l'héritage mais implicite, non?

sauf qu'ici c'est la classe mère qu'on modifie directement et non une simple classe fille ayant fonctionnalités en plus.
En effet une classe fille se contente d'hériter des attributs/méthodes de la classe mère, mais une classe fille peut-elle agir directement sur les méthodes et attributs de la classe mère? j'en doute ^^

Qu'est-ce qui t'empêche de redéfinir une méthode de la classe mère dans la classe fille? C'est bien ce qu'on fait là non?

Oui, c'est bien ce qu'on fait.
JE dirais juste que le MP est plus direct, alors que l'héritage nécéssite la création de classe fille avec la syntaxe de l'héritage etc...
Mais c'est vrai que dans le fonctionnement ça y ressemble ;P

@furankun

En fait le MonkeyPatching c'est un peu comme de l'héritage mais implicite, non?

Je comprend ton raisonnement, mais j'attire vraiment ton attention sur les commentaires de @boblinux.

Les deux n'ont pas de rapport entre eux, ne sont pas interchangeables et utiliser l'un a la place de l'autre serait désastreux.

  • L'héritage te sers a structurer ton code, le polymorphisme te sers a définir des comportements différents pour chaque sous classe. L'idée c'est de spécialiser un peu plus le code a chaque fois que tu créer une nouvelle classe fille tout en bénéficiant d'un code plus générique définie dans la classe mere. La nuance c'est que tu ne remplace pas le code de la classe mere, tu le surcharge. Le code de la classe mere continue a d'éxister.
  • Le monkey patch te sers a modifier du code sur lequel tu n'as pas la main en l'écrasant. Il faut garder en tete que c'est pas tres propre et a réserver aux cas ou tu n'as pas d'autre choix.

Là c'est clair, merci!

+5 votes

Pour compléter la réponse de @boblinux :

Un décorateur sert a englober un appel a une fonction (ou méthode). Tu pourras modifier ce qui se passe avant et apres l'appel (tout comme avec un context manager), mais pas son fonctionnement interne.
C'est utile par exemple pour initialiser des ressources et les relacher avant/apres, catcher certaines exceptions, logguer un temps d'éxecution, modifier les parametres passé a la fonction...

Le monkey patching te sera utile pour modifier le fonctionnement interne d'une fonction : tu redéfinis (réécrit) son comportement. Tu écris une fonction qui remplace celle écrite initialement.
Tu ne pourras pas le faire partout : certaines méthodes ne sont pas réaffectables (de mémoire les méthodes définies dans des modules écrit en C)

répondu 27-Mai-2015 par jc (2,704 points)

Le monkey patching te sera utile pour modifier le fonctionnement
interne d'une fonction

T'as voulu dire classe non? ;P
Modifier une fonction c'est un peu bizarre, sauf si c'est une fonction d'une fonction mais une fonction sans méthode ni attributs (qui sont spécifiques aux classes) me paraît un peu étrange à modifier.

Non, c'est bien ce que j'ai voulu dire mais j'avoue un abus de langage : tu remplaces la fonction et tu modifie son comportement. Tu modifie son fonctionnement interne d'un point de vue objet, par opposition aux décorateurs avec lequels tu englobes son execution.

Modifier une fonction c'est un peu bizarre

Effectivement, ici tu ne modifies pas l'instance d'objet fonction que tu monkeypatch, tu remplaces sa référence.

sauf si c'est une fonction d'une fonction mais une fonction sans méthode ni attributs

Ca j'ai pas compris

(qui sont spécifiques aux classes)

Et ca non plus ;)

Ah ok, autant pour moi.
Vu que tout est objet en python, donc si on prend les fonctions d'un point de vue objet c'est bon x).
Par contre, t'aurais pu mettre un petit exemple ça aurait été un tout petit plus clair (en plus de la clarté de ta réponse =D )

Je ne comprend pas : en quoi le fait que la fonction soit un objet change quelque chose ?

Bah je parle juste de :

Tu modifie son fonctionnement interne d'un point de vue objet

C'est pour ça que si tu pouvais citer un exemple par rapport à la modification du fonctionnement interne d'une fonction je pense que ce serait plus clair.
N'est-ce pas qu'un bon exemple vaut mieux qu'un long discours ? =D
(wé on a tous entendu cette réplique des profs mais il arrive qu'ils ne disent pas que des conneries... x: )

Désolé, poser du code pour poser du code n'as aucun sens.

Ici il s'agit du concept objet. Du point de vue du concept objet.
Est-ce que c'est plus clair avec des mots en gras ?

Je te retourne l'invitation. Pourrais-tu nous donner un exemple concret de

une fonction d'une fonction mais une fonction sans méthode ni attributs (qui sont spécifiques aux classes)

Bon but ce n'est pas d'avoir le dernier mot je te rassure x).

Peut-être bien que tu as raison, je pensais que le MP ne servait qu'a modifier les classes, je n'arrive pas à m'imaginer un exemple de MP avec une fonction.

C'est juste pour ça que je t'ai demandé cette question, pour que je puisse en apprendre d'avantage par l'exemple concret.

Désolé si je t'ai un peu embrouillé dans les com's, moi même je me suis embrouillé je te rassure.

Donc il se peut bien que j'ai dit de la m...

Cordialement x)

Ok, je pense qu'il y a incompréhension sur les termes employés:

  • d'un coté tu modifies la structure de la classe en changeant une de ses méthodes. Mais tu peut modifier aussi un module, une instance....
  • d'un autre tu modifies le comportement d'une méthode en la réecrivant : c'est ce que je décrivait en parlant du fonctionnement interne de la fonction (ou méthode).

Tu peut monkey patcher presque tout et n'importe quoi.

Si t'as un module qui définit une fonction, tu peut tres bien la remplacer.
Tu peu aussi remplacer une classe par une autre.

Admettons t'as le module mp.py suivant :

def function_a():
    return "Original"

class A():
    def method_a(self):
        return "Original"

Dans ton shell :

import mp
a = mp.A()
print(a.function_a())
Original

# Patch au niveau instance
a.function_a = lambda : "Instance a patched"

b = mp.A()
# B a toujours la methode originale
print(b.method_a())

# Patch au niveau classe
mp.A.method_a = lambda self: "Class patched"
# les 2 instances sont impactées
print(a.method_a())
Class patched
print(b.method_a())
Class patched

# Ici mode brutal, on remplace une classe entière
class B():pass
# on monkey patch B pour qu'il ait la meme interface que A
B.method_a = lambda self: "Lambda patch"
mp.A = B # Tu remplace la classe A par B
c = mp.A()
print(c.method_a())
Lambda patch

# Et meme chose pour les fonctions
mp.function_a = lambda : "Function patched"

print(mp.function_a())
Function patched

J'ai utilisé des lambdas pour réduire la taille de code, c'est excactement la meme chose en utilisant des fonctions a la place.

Tu peux faire a peu pret tout ce que tu veux, c'est ce qui fait que c'est pratique (et dangeureux).

cool, merci @Jc
Je pense qu'on devrait ouvrir un post pour proposer tous les cas où il est dangereux de MonkeyPatcher, histoire d'appuyer encore plus la clarté de ce fil intéréssant sur le MP .

...