Voici une maquette de ce que je veux faire:Python: Comment modifier dynamiquement les méthodes des objets dict et list?
alist = [1,2,3,4,5] # create a vanilla python list object
replacef (alist) # replace __setitem__, extend,... with custom functions
alist[0]=2 # now the custom __setitem__ is called
Ceci est pour un projet DSL où la syntaxe doit être aussi proche de la liste python normale que possible, le sous-classement ainsi et de faire l'appel de l'utilisateur quelque chose comme alist = MyList (1,2,3,4,5) n'est pas souhaitable. En outre, parce que le DSL doit coexister avec d'autres bibliothèques, liste et changer le monde dict est pas une option ...
J'ai essayé de créer instancemethods et les mettre directement sur l'objet comme alist.append = myFunc, mais dit Python ces propriétés sont en lecture seule. La modification de l'attribut __class__
ne semble pas non plus autorisée.
Est ce que j'essaye de faire même possible en Python?
Mise à jour: voici quelques conclusions sur ce qui est possible avec une sous-classe de l'objet:
Vu:
class y(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def f(self):
print self
print self.a
print self.b
print self.c
class x(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def f(self):
print "x.f()"
>>> objy = y(1,2,3)
>>> objx = x(4,5,6)
>>> objy.f()
<__main__.y object at 0x02612650>
1
2
3
>>> objx.f()
x.f()
Il est possible de changer la classe de objx à la volée comme ceci:
>>> objx.__class__ = y
>>> objx.f()
<__main__.y object at 0x02612D90>
4
5
6
en outre, on peut ajouter dynamiquement/modifier les méthodes d'objets à la volée, un peu comme en javascript:
>>> def g(self,p):
print self
print p
>>> import new
>>> objy.g = new.instancemethod(g,objy,y)
>>> objy.g(42)
<__main__.y object at 0x02612650>
42
Cependant, les deux approches échouent avec des objets qui sont mis en œuvre en C comme dict et liste:
>>> def mypop(self):
print "mypop"
list.mypop(self)
>>> alist.pop = new.instancemethod(mypop,alist,list)
AttributeError: 'list' object attribute 'pop' is read-only
>>> x = [1,2,3,4,5]
>>> x.__class__ = mylist
TypeError: __class__ assignment: only for heap types
L'approche proposée d'utiliser quelque chose comme alist = replacef (alist) ne fonctionnerait pas ici, parce que replacef pourrait être appelé à partir d'un appel __setattribute__
comme ceci:
alist = [1,2,3,4,5]
aDSLObject.items = alist # __setattribute__ calls replacef on alist
alist[0] = ....
Ainsi, alors qu'il est possible de changer les méthodes d'objet, il semble en effet dynamique comme il est impossible t o modifier le comportement des objets implémentés en C - ou est-ce que je manque quelque chose?
+1: Solution douce à un problème étrange. Une syntaxe de constructeur légèrement différente ('MyList' ou' [') n'est pas un obstacle à l'acceptation, alors pourquoi s'inquiéter? Mais cela résout. –
Merci pour votre réponse. C'est en effet un problème étrange je suppose :) Idéalement, l'utilisateur de la DSL ne devrait pas avoir à connaître l'implémentation sous-jacente. J'aime bien l'approche. Peut-être y a-t-il un moyen de changer ce que 'alist' pointe sans assignation directe, comme un 'pointeur vers un pointeur' en C? –
Non, je ne pense pas que ce soit possible. La chose la plus proche est probablement de changer le contenu de la variable mutable, par exemple la liste. BTW si vous avez réussi à augmenter les occurrences de la liste avec un nouveau comportement sans changer la classe - ce serait un peu effrayant. S'il y a du code qui utilise type() et isinstance() et autres, il ne remarquerait pas la différence et causerait peut-être des effets secondaires involontaires ... –