Je ne connais pas une façon de faire ce que vous demandez - remplacer les mutateurs sans les surcharger. Avec un décorateur de classe, cependant, vous pouvez "automatiser" les versions dominantes (en supposant que chacune peut être obtenue en enveloppant la méthode correspondante dans la classe de base), donc ce n'est pas trop ...
Supposons par exemple que ce que vous vouloir faire est ajouter un drapeau "modifié", vrai si les données peuvent avoir été modifiées depuis le dernier appel à .save
(une de vos méthodes qui persistent les données et définit self.modified
à False).
Alors ...:
def wrapMethod(cls, n):
f = getattr(cls, n)
def wrap(self, *a):
self.dirty = True
return f(self, *a)
return wrap
def wrapListMutators(cls):
for n in '''__setitem__ __delitem__ __iadd__ __imul__
append extend insert pop remove reverse sort'''.split():
f = wrapMethod(cls, n)
setattr(cls, n, f)
return cls
@wrapListMutators
class DataSet(list):
dirty = False
def save(self): self.dirty = False
Cette syntaxe nécessite Python 2.6 ou mieux, mais, dans les versions antérieures Python (ceux qui ne supportent que les décorateurs sur def
déclarations, et non sur class
déclarations, ou même très vieux qui ne supportent pas les décorateurs du tout), il vous suffit de changer la dernière partie (la déclaration class
), à:
class DataSet(list):
dirty = False
def save(self): self.dirty = False
DataSet = wrapListMutators(DataSet)
OIEau, la syntaxe décorateur propre est juste une petite quantité de sucre de syntaxe au-dessus d'un appel de fonction normale qui prend la classe comme argument et la réaffecte.
Edit: maintenant que vous avez modifié votre question pour clarifier vos besoins exacts - maintenir sur chaque élément un champ bp
tel que, pour tous i
, theset[i].bp == i
- il est plus facile de peser les avantages et les inconvénients des différentes approches.
Vous pouvez adapter l'approche que je dessinais, mais au lieu de self.dirty
affectation avant l'appel à la méthode enveloppée, ont un appel self.renumber()
après, à savoir:
def wrapMethod(cls, n):
f = getattr(cls, n)
def wrap(self, *a):
temp = f(self, *a)
self.renumber()
return temp
return wrap
cela répond à vos exigences énoncées, mais dans de nombreux cas, il fera beaucoup plus de travail que nécessaire: par exemple, lorsque vous append
un élément, cela renumérote inutilement tous les existants (aux mêmes valeurs qu'ils avaient déjà). Mais comment une approche entièrement automatisée peut-elle "savoir" quels éléments, le cas échéant, elle doit recalculer le .bp
de, sans effort O(N)
? Au moins, il doit regarder chacun d'entre eux (puisque vous ne voulez pas coder séparément, par exemple, append
vs insert
& c), et c'est déjà O(N)
.
Cela ne sera donc acceptable que si toutes les modifications apportées à la liste sont acceptées O(N)
(essentiellement si la liste reste toujours petite et/ou ne change pas souvent).
Une idée plus fructueuse pourrait être de ne pas maintenir .bp
valeurs tout le temps, mais seulement "juste à temps" lorsque cela est nécessaire.Rendre bp
une propriété (en lecture seule), en appelant une méthode qui vérifie si le conteneur est "sale" (où le drapeau "sale" dans le conteneur est maintenu en utilisant le code automatisé que j'ai déjà donné) et seulement renomme le conteneur (et définit son attribut "dirty" à False
). Cela fonctionnera bien quand la liste est généralement soumise à une rafale de changements et seulement alors vous devez accéder aux éléments 'bp
pendant un moment, puis un autre groupe de changements, etc. Une telle alternance entre changement et lecture n'est pas rare dans les conteneurs du monde réel, mais vous seul pouvez savoir si cela s'applique dans votre cas particulier!
Pour obtenir des performances au-delà de cela, je pense que vous devez faire un codage manuel en plus de cette approche générale pour tirer parti des cas spéciaux fréquents. Par exemple, append
peut être appelé très souvent, et la quantité de travail à faire dans un cas particulier append
est vraiment faible, il peut donc être utile d'écrire ces deux ou trois lignes de code (ne pas définir le bit sale pour ce cas). Un inconvénient: aucune approche ne fonctionnera (en effet votre exigence devient contradictoire) si un élément est présent deux fois dans la liste - ce qui bien sûr est parfaitement possible, sauf si vous prenez des précautions pour l'éviter (vous pouvez facilement le diagnostiquer dans renumber
- en conservant un ensemble d'éléments déjà vus et en levant une exception sur toute duplication - si ce n'est pas trop tard pour vous, il est plus difficile de diagnostiquer "à la volée", c'est à dire lors d'une mutation qui provoque une duplication, si c'est ce dont vous avez besoin). Peut-être que vous pouvez assouplir votre exigence de sorte que, si un article est présent deux fois, c'est OK et le bp
peut simplement indiquer l'un des indices; ou faire bp
dans un définir des indices où l'élément est présent (cela offrirait également une approche en douceur pour le cas d'obtenir bp
à partir d'un élément qui est pas dans la liste). Etc; Je vous recommande de considérer (et document!) Tous ces cas d'angle en profondeur - l'exactitude avant la performance!
Comme Alex a répondu, il semble difficile. Mais peut-être que les gens peuvent trouver une solution élégante si vous nous expliquez ce que devrait faire l'insertion substituée. – NicDumZ
Je pense qu'il fait référence au patch-singe. – Geo
@NicDumZ: J'ai mis à jour la question –