Y a-t-il un moyen de faire une liste code> appeler une fonction à chaque fois que la liste est modifiée?
Par exemple: P>
>>>l = [1, 2, 3] >>>def callback(): print "list changed" >>>apply_callback(l, callback) # Possible? >>>l.append(4) list changed >>>l[0] = 5 list changed >>>l.pop(0) list changed 5
3 Réponses :
Je suis presque certain que cela ne peut pas être fait avec la liste standard. P>
Je pense que le moyen le plus propre serait d'écrire votre propre classe pour le faire (peut-être hériter de la liste code>). p>
Vous auriez à la sous-classe comme indiqué dans les commentaires, c'est plus que juste Vous pourriez même être mieux servi en construisant un objet qui implémente la liste code> interface code> et ajoute de manière dynamique les descripteurs à la place de la Machines de liste normales. Ensuite, vous pouvez réduire vos appels de rappel à seulement le descripteur list code> et modifier
__ settitem __ code>.
__ settitem __ code>. p>
__ obtenez __ code>,
__ ensemble __ code> et
__ Supprimer __ code>. P> P>
et __ delitem __ code> et
pop code> et
supprimer code> et
insérez code> et
appendez code> et
Étendre code> et
Trier code> et
inverse code> ... c'est beaucoup de travail, mais au moins c'est une chose assez triviale à ajouter ...
@ Syeryksun - Je viens de voir ceux de la liste des méthodes de liste Python aussi :)
Peut-être simplement modifier __ getattribute __ code> avec une liste de toutes les fonctions concernées ...;)
@pythonm: Il suffit de recevoir l'attribut que vous avez appelé la méthode.
Je voulais dire quelque chose comme si attr dans self.relevantgmethods: rappel (); liste de retour .__ getattribute __ (auto, attr) code>
Emprunter à partir de la suggestion de @ SR2222, voici ma tentative. (Je vais utiliser un décorateur sans le sucre syntaxique):
import sys _pyversion = sys.version_info[0] def callback_method(func): def notify(self,*args,**kwargs): for _,callback in self._callbacks: callback() return func(self,*args,**kwargs) return notify class NotifyList(list): extend = callback_method(list.extend) append = callback_method(list.append) remove = callback_method(list.remove) pop = callback_method(list.pop) __delitem__ = callback_method(list.__delitem__) __setitem__ = callback_method(list.__setitem__) __iadd__ = callback_method(list.__iadd__) __imul__ = callback_method(list.__imul__) #Take care to return a new NotifyList if we slice it. if _pyversion < 3: __setslice__ = callback_method(list.__setslice__) __delslice__ = callback_method(list.__delslice__) def __getslice__(self,*args): return self.__class__(list.__getslice__(self,*args)) def __getitem__(self,item): if isinstance(item,slice): return self.__class__(list.__getitem__(self,item)) else: return list.__getitem__(self,item) def __init__(self,*args): list.__init__(self,*args) self._callbacks = [] self._callback_cntr = 0 def register_callback(self,cb): self._callbacks.append((self._callback_cntr,cb)) self._callback_cntr += 1 return self._callback_cntr - 1 def unregister_callback(self,cbid): for idx,(i,cb) in enumerate(self._callbacks): if i == cbid: self._callbacks.pop(idx) return cb else: return None if __name__ == '__main__': A = NotifyList(range(10)) def cb(): print ("Modify!") #register a callback cbid = A.register_callback(cb) A.append('Foo') A += [1,2,3] A *= 3 A[1:2] = [5] del A[1:2] #Add another callback. They'll be called in order (oldest first) def cb2(): print ("Modify2") A.register_callback(cb2) print ("-"*80) A[5] = 'baz' print ("-"*80) #unregister the first callback A.unregister_callback(cbid) A[5] = 'qux' print ("-"*80) print (A) print (type(A[1:3])) print (type(A[1:3:2])) print (type(A[5]))
Les rappels devraient s'attendre à soi-même, à l'opération et aux données pertinentes pour l'opération, je pense. Sinon, très gentil.
@ SR2222 - Je pense que les rappels ne devraient s'attendre à rien, bien qu'il soit suffisamment facile de changer de recommandation (à gauche en tant qu'exercice pour le lecteur curieux). Cependant, la fourniture d'informations supplémentaires à un rappel est généralement accomplie via des fermetures et lambda code>. En effet, je pense que c'est une raison pour laquelle
lambda code> est donc i> important dans la langue. C'est comme ça que c'est fait dans
tkinter code> par exemple.
Je pense que je pense que vous voulez que cela soit utile comme plus qu'un jouet, vos rappels veulent pouvoir être au courant de la liste et les choses qui y avaient changé.
@ SR2222 - Peut-être. Cela signifie que la liste est vraiment facile, cependant: A.ADD_Callback (Lambda: rappel (a)) code>. Je suppose que l'avoir conscient de ce qui est changeant est un peu plus difficile sans changer le cadre.
Dans 2.x, il y a aussi __ setslice __ code> et
__ delslice __ code>.
En fait, pas vraiment, maintenant que j'y pense. Il suffit de faire rappel (auto, func, * args, ** kwargs) code> (ou peut-être
func .__ Nom __ code>) et votre callback sait tout.
Vous voudrez peut-être aussi remplacer __ getlice __ code> (et
__ getItem __ code> in 3.x, pour
tranche code> objets) pour renvoyer un
notifique code >.
@ Syeryksun - C'est une suggestion raisonnable et bon appel sur __ setslice __ code> et
__ delslice __ code>. Je pensais que ceux-ci seraient géré par
__ settitem __ code> et
__ delitem __ code> (ils sont généralement) ...
Python 2.x a des ops spécifiques de bytecode pour la tranchée. Les méthodes de séquence associées sont obsolètes mais toujours utilisées par des types intégrés. Voici la fonction assign_slice code> dans CEval. C . Il essaie d'abord d'utiliser la méthode de séquence
sq_ass_slice code> (méthode de liste
list_ass_slice code>), alors qu'il construit un
tranche code> un objet.
@ Syeryksun - J'ai mis à jour avec vos suggestions. J'ai testé (brièvement) avec PY2K et PY3K. N'hésitez pas à éditer / commenter si vous avez d'autres suggestions.
@ Syeryksun - L'existence des diverses méthodes __ code> semble plus stupide par la seconde. Quoi qu'il en soit, j'ai mis à jour, a ajouté quelques tests supplémentaires qui semblent bien. Aussi, j'ai ajouté un moyen de désinscrire les rappels qui avaient été enregistrés auparavant - il m'a plus ou moins forcé de changer la liste de rappel à une dicte (ce que je ne suis pas terriblement heureux de).
Ma réponse originale était également manquante
__ iadd __ code> et
__ imul __ code> :). Je viens de les ajouter.