Bonjour.
J'essaie de créer un proxy en python. Le but est de pouvoir utiliser mon objet comme l'objet qu'il encapsule.
J'ai commencé par ça (le code n'est peut-être pas propre, plein de commentaires oui d'affichage bidon, mais c'est pour voir ce qui se passe : il ne passera pas en prod mais me sert à comprendre ce qui se passe):
#!/usr/bin/python
class MyClass(object):
def __init__(self,_class=None):
print("Initialization of class MyClass without args\n")
o=_class()
print("Type object : " + str(o.__class__()))
self.__dict__['_obj']=o
def __getattr__(self,name):
def _missing(*args, **kwargs):
print "A missing method was called."
print "The object was %r, the method was %r. " % (self, name)
print "It was called with %r and %r as arguments" % (args, kwargs)
return getattr(self.__dict__['_obj'],name)
return _missing
c=MyClass(list)
x=c.append()
print ("value : " + str(c[0]))
A l'exécution, j'ai ce message :
./test1.py
Initialization of class MyClass without args
Type object : []
A missing method was called.
The object was <__main__.MyClass object at 0x7f9c4aa63ad0>, the method was 'append'.
It was called with () and {} as arguments
Traceback (most recent call last):
File "./test1.py", line 27, in <module>
print ("value : " + str(c[0]))
TypeError: 'MyClass' object does not support indexing
Ce que j'en ai compris, c'est que je n'ai pas implémenté la méthode __getitem__
dans ma classe.
Or, je croyais que la méthode non implémentée serait intercptée par __getattr__
, mais il semble que je me sois trompé.
Y aurait-il un autre niveau ou je pourrais intercepter toutes les méthodes définies, pour pouvoir les rediriger vers mon objet ?
En ruby, je peux le faire :
#!/usr/bin/ruby
#
#
class Test
def initialize()
end
def initialize(obj)
@obj=obj.new()
end
def method_missing(meth,*args,&block)
puts("Method: #{meth}, args : #{args}")
@obj.send(meth.to_sym,*args)
end
end
x=Test.new(Array)
x.push("abc")
x.push("def")
puts("x[0]: #{x[0]}")
Le résultat :
$ ruby test1.rb
Method: push, args : abc
Method: push, args : def
Method: [], args : 0
x[0]: abc
Merci d'avance.
# encapsulation ou héritage ?
Posté par Marc Quinton . Évalué à 2.
je vois que tu es passé par l'encapsulation pour faire ton proxy. Mais ca pourrait aussi se faire par le biais de l'héritage, sauf qu'il faut connaître à l'avance la classe de l'objet. Dans ton cas, dans la class Test, en ruby, tu passes la class, puis c'est une instance d'objet qui est faite.
[^] # Re: encapsulation ou héritage ?
Posté par totof2000 . Évalué à 2.
Je ne préfère pas passer par l'héritage : je souhaite ajouter d'autres propriétés à ma classe. Entre autres, je souhaiterais pouvoir ajouter un lock pour permettre à mes classes encapsulées d'être thread_safe, plus d'autres infos, mais en gardant le comportement "normal" de l'objet encapsulé : ici c'est une liste mais j'ai d'autres objets à utiliser de cette façon.
[^] # Re: encapsulation ou héritage ?
Posté par totof2000 . Évalué à 2.
Désolé, je ne comprends pas ce que tu veux dire. Peut-être parce qu'il est tard et que j'ai passé une bonne partie de la journée à chercher une solution à ce problème. Pourrais-tu détailler un peu STP ?
# quelques remarques
Posté par gaaaaaAab . Évalué à 2. Dernière modification le 18 avril 2014 à 19:35.
Attention,
__getattr__
est censé renvoyé la valeur d'un attribut. Ton implémentation actuelle renvoie une méthode qui renvoie la valeur de l'attribut pour _obj. Est-ce que tu ne voudrais pas plutôt faire ? :Pour l'appel direct c[0], de ce que j'ai testé, l'interpréteur utilise la méthode
__getitem__
, mais en l'appelant directement, sans passer par__getattr__
. Du coup, tu peux t'en sortir en ajoutant la méthode:Si tu veux pouvoir affecter un élément de la liste, il faut que tu ajoutes aussi
__setitem__
. Bref, pour faire propre, il faudrait ajouter tous les__<methode>__
de la classe qui t'intéresse (inclus__repr__
et__str__
pour faire joli).Mais du coup, tu te retrouves avec un proxy spécialisé pour les listes, ce qui n'est pas forcément ce que tu veux. L'étape d'après est de lister tous les attributs de la classes que tu veux proxifier, et rajouter les méthode
__*__
dont tu as besoin à l'instanciation. Et du coup, il y a surement une recette quelque part sur le net.Edit: je vois que je répond un peu à côté de la question sur les
__getitem__
et compagnie[^] # Re: quelques remarques
Posté par totof2000 . Évalué à 2.
C'est justement ce que je n'ai pas envie de faire.
Exactement. Je voudrais pouvoir faire ça sur d'autres objets.
Si elle existe, je ne l'ai pas vue, mais j'ai peut-être mal cherché.
Non, en fait tu es en plein dedans : tu as compris exactement ce que je voulais. En fait je voudrais quelque chose d'applicable à toute méthode de l'instance à proxifier. J'ai eu un problème avec getitem, mais j'ai vite compris que j'aurais des problèmes avec d'autres méthodes, et je recherche une méthode "générique", comme l'exemple que j'ai montré en Ruby.
En tout cas merci : même si tu n'as pas proposé de solution à mon problème, tu l'as clairement identifié.
[^] # Re: quelques remarques
Posté par gaaaaaAab . Évalué à 2. Dernière modification le 19 avril 2014 à 01:12.
En fait, j'ai pas trouvé non plus, mais c'est p-e parce que c'est pas comme ça qu'on ferait en Python. Tu peux peut-être t'en sortir avec un décorateur qui fabriquerait à la volée des classes dérivées des types qui t'intéressent avec les ajustements dont tu as besoin. Autrement dit, au lieu d'encapsuler ta classe dans un proxy générique, est-ce que ça serait pas plus simple d'avoir un générateur de classes proxy spécialisées ?
[^] # Re: quelques remarques
Posté par totof2000 . Évalué à 2.
C'est une des raisons pour lesquelle je n'aime pas Python ( et Java également) : il complique les choses qui se font de façon simple et évidentes dans d'autres langages. J'avais dit à une époque que je trouvais que Python était un langage qui force à se répéter, et voici un exemple typique de ce que je voulais dire. Entendons-nous bien : je ne veux pas "cracher" sur Python. J'avais juste promis il y a quelques temps que je donnerais des raisons concrètes pour lesquelles je trouvais que Python force à se répéter, et en voilà un concret.
Plus simple, je ne pense pas , d'autant plus que je n'ai pas forcément la maîtrise des classes sous-jacentes .. a moins que je puisse définir dans mon proxy, et de façon automatique, une méthode correspondant à chaque méthode définie dans l'objet. Mais ai-je un moyen de savoir si les méthodes magiques sont implémentées ?
Sinon je ferai un proxy spécialisé pour mon besoin immédiat (ou je ne ferai pas de proxy du tout). Je verrai bien. Mais je trouve ça gênant.
[^] # Re: quelques remarques
Posté par gaaaaaAab . Évalué à 4.
En farfouillant sur le net, j'ai trouvé des infos supplémentaires.
cf le premier message de http://www.gossamer-threads.com/lists/python/dev/651981
Si tu es en Python 2, tu peux t'en sortir simplement en ne dérivant pas ta classe de
object
(j'ai testé, ça marche).Sinon, j'ai aussi trouvé une recette pour python 3. Cette recette fonctionne un peu différemment parce qu'elle crée des proxy sur des objets instanciés, et pas sur des classes. En reprenant l'exemple, comme ça, chémoisamarche :
oui, la fonction
dir
liste tous les attributs de ce qu'on lui passe en paramètres.et vice et versa !
ça ne correspond pas à mon expérience. C'est vrai qu'en étant confronté à des nouveaux problèmes, il m'a parfois fallu un peu de temps pour trouver comment faire, mais j'ai toujours réussi à atteindre un truc que je trouvais propre en Python.
raison suivante stp (ou un autre exemple de répétition) :-)
[^] # Re: quelques remarques
Posté par totof2000 . Évalué à 2.
Je vais regarder ça. En tout cas, merci pour ton aide.
[^] # Re: quelques remarques
Posté par totof2000 . Évalué à 2.
Ca marche effectivement en ne dérivant pas ma classe de object. J'avais ajouté ça à un moment pour éviter les boucles infinies en appelant le parent dans la méthode initialize. En nettoyant mon code par rapport à ce que j'ai fait, ça fonctionne.
Merci encore.
[^] # Re: quelques remarques
Posté par totof2000 . Évalué à 2.
Désolé, j'ai oublié de répondre à ça :
Je t'avouerais que j'ai testé des trucs dans tous les sens, et que comme je l'ai dit plus haut, certaines choses se sont greffées par dessus. Ce soir je ne sais plus trop ou j'en suis et il est fort probable que tu aies raison. Je testerai Lundi (et merci au passage pour cette correction - sinon je te dirai ce que je veux faire exactement).
```
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.