49 votes

Pouvez-vous utiliser des méthodes de patch sur les types principaux en python?

Ruby peut ajouter des méthodes à la classe de Nombre et d'autres types de base pour obtenir des effets tels que:

1.should_equal(1)

Mais il semble que python ne peut pas faire cela. Est-ce vrai? Et si oui, pourquoi? A-t-elle quelque chose à voir avec le fait que le type ne peut pas être modifié?

Mise à jour: Plutôt que de parler des différentes définitions de monkey patching, je voudrais me concentrer sur l'exemple ci-dessus. J'ai déjà conclu qu'il ne peut être fait que quelques-uns d'entre vous ont répondu. Mais je voudrais une explication plus détaillée des raisons pour lesquelles il ne peut pas être fait, et peut-être quelle est la caractéristique, si elle est disponible en python, permettrait à ce.

Pour répondre à certains d'entre vous: La raison pour laquelle je peut veux faire c'est simplement de l'esthétique et la lisibilité.

de l'élément.prix.should_equal(19.99)

se lit plus comme l'anglais et indique clairement qui est la valeur testée et qui est la valeur attendue, comme on le suppose à:

should_equal(item.prix, 19.99)

Ce concept est ce que Rspec et quelques autres Ruby cadres sont basées sur.

70voto

John Millikin Points 86775

Non vous ne pouvez pas. En Python, toutes les données (classes, méthodes, fonctions, etc.) définies dans les modules d’extension C (y compris les éléments intégrés) sont immuables. Cela est dû au fait que les modules C sont partagés entre plusieurs interprètes dans le même processus. Par conséquent, leur correspondance unique affecterait également les interprètes non liés dans le même processus.

Cependant, les classes définies dans le code Python peuvent être monkeypatchées car elles sont locales à cet interpréteur.

42voto

Dan Points 18831

Que voulez-vous dire exactement par Monkey Patch ici? Il y a plusieurs légèrement différentes définitions.

Si vous voulez dire, "pouvez-vous modifier une classe de méthodes au moment de l'exécution?", alors la réponse est catégoriquement oui:

class Foo:
  pass # dummy class

Foo.bar = lambda self: 42

x = Foo()
print x.bar()

Si vous voulez dire, "pouvez-vous modifier une classe de méthodes lors de l'exécution et de faire toutes les instances de ce changement de classe après le fait? ", alors la réponse est oui ainsi. Il suffit de changer l'ordre légèrement:

class Foo:
  pass # dummy class

x = Foo()

Foo.bar = lambda self: 42

print x.bar()

Mais vous ne pouvez pas le faire pour certaines classes intégrées, comme int ou float. Ces méthodes de classes sont implémentés en C et il y a certaines abstractions sacrifié afin de rendre l'application plus facile et plus efficace.

Je ne suis pas vraiment clair sur pourquoi vous voulez modifier le comportement du numérique intégré les classes de toute façon. Si vous avez besoin de modifier leur comportement, une sous-classe!!

29voto

Jonathan Points 109
 def should_equal_def(self, value):
    if self != value:
        raise ValueError, "%r should equal %r" % (self, value)

class MyPatchedInt(int):
    should_equal=should_equal_def

class MyPatchedStr(str):
    should_equal=should_equal_def

import __builtin__
__builtin__.str = MyPatchedStr
__builtin__.int = MyPatchedInt

int(1).should_equal(1)
str("44").should_equal("44")
 

S'amuser ;)

26voto

alcalde Points 376

Vous pouvez faire cela, mais il faut un peu de piratage. Heureusement, il existe un module appelé "Fruit Défendu" que vous donne le pouvoir de patch méthodes de types intégrés de manière très simple. Vous pouvez le trouver à

http://clarete.github.io/forbiddenfruit/?goback=.gde_50788_member_228887816

ou

https://pypi.python.org/pypi/forbiddenfruit/0.1.0

Avec la question d'origine exemple, après l'écriture de la "should_equal" fonction, vous devriez juste faire

from forbiddenfruit import curse
curse(int, "should_equal", should_equal)

et vous êtes bon pour aller! Il y a aussi une fonction "reverse" pour supprimer une version modifiée de la méthode.

13voto

zaphod Points 1980

Python types de base sont immuables, de par sa conception, comme d'autres utilisateurs l'ont souligné:

>>> int.frobnicate = lambda self: whatever()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'int'

Vous avez certainement pu obtenir l'effet que vous décrivez en faisant une sous-classe, puisque les types définis par l'utilisateur en Python sont mutables par défaut.

>>> class MyInt(int):
...   def frobnicate(self):
...     print 'frobnicating %r' % self
... 
>>> five = MyInt(5)
>>> five.frobnicate()
frobnicating 5
>>> five + 8
13

Il n'y a pas besoin de faire de l' MyInt sous-classe publique, soit; on pourrait tout aussi bien définir inline directement dans la fonction ou de la méthode que des constructions de l'instance.

Certainement, il y a peu de situations où Python pour les programmeurs qui sont à l'aise dans l'idiome d'envisager ce type de sous-classement, la bonne chose à faire. Par exemple, os.stat() renvoie un tuple sous-classe qui ajoute les membres nommés, afin précisément d'aborder le genre de la lisibilité de l'inquiétude vous vous référez dans votre exemple.

>>> import os
>>> st = os.stat('.')
>>> st
(16877, 34996226, 65024L, 69, 1000, 1000, 4096, 1223697425, 1223699268, 1223699268)
>>> st[6]
4096
>>> st.st_size
4096

Cela dit, dans l'exemple que vous donnez, je ne crois pas que le sous-classement float en item.price (ou ailleurs) serait très probablement être considéré comme le Pythonic chose à faire. Je peux facilement imaginer quelqu'un de décider d'ajouter un price_should_equal() méthode de item si c'était le cas d'utilisation principal; si l'on était à la recherche de quelque chose de plus général, peut-être qu'il pourrait faire plus de sens d'utiliser des arguments nommés pour rendre le sens plus clair, que dans

should_equal(observed=item.price, expected=19.99)

ou quelque chose le long de ces lignes. C'est un peu verbeux, mais nul doute qu'elle pourrait être améliorée. Un possible avantage d'une telle approche sur Ruby-style singe brassage est qu' should_equal() pourrait facilement effectuer une comparaison sur n'importe quel type, pas seulement int ou float. Mais peut-être que je suis trop pris dans les détails de l'exemple particulier qui vous est arrivé, à fournir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X