Я не знаю, как сделать то, что вы просите - переопределить мутаторы, не переопределяя их. Однако с помощью декоратора классов вы можете «автоматизировать» переопределяющие версии (при условии, что каждая из них может быть достигнута путем помещения соответствующего метода в базовый класс), так что это не так уж плохо ...
Предположим, например, что вы хотите добавить «модифицированный» флаг, true, если данные могли быть изменены со времени последнего вызова .save
(ваш метод, который сохраняет данные и устанавливает self.modified
ложно).
Тогда ...:
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
Этот синтаксис требует Python 2.6 или выше, но в более ранних версиях Python (те, которые поддерживают декораторы только в операторах def
, но не в class
; или даже очень старые, которые вообще не поддерживают декораторы) вам просто нужно изменить самую последнюю часть (оператор class
) на:
class DataSet(list):
dirty = False
def save(self): self.dirty = False
DataSet = wrapListMutators(DataSet)
IOW, аккуратный синтаксис декоратора - это просто небольшое количество синтаксического сахара поверх обычного вызова функции, который принимает класс в качестве аргумента и переназначает его.
Редактировать: теперь, когда вы отредактировали свой вопрос, чтобы уточнить свои точные требования - оставьте для каждого элемента поле bp
, чтобы для всех i
, theset[i].bp == i
- было легче взвесить за и против различных подходов.
Вы могли бы адаптировать набросок, который я набросал, но вместо self.dirty
присваивания перед вызовом метода-обертки, после него должен быть вызов self.renumber()
, т.е.
def wrapMethod(cls, n):
f = getattr(cls, n)
def wrap(self, *a):
temp = f(self, *a)
self.renumber()
return temp
return wrap
это соответствует вашим заявленным требованиям, но во многих случаях оно будет выполнять гораздо больше работы, чем необходимо: например, когда вы append
элемент, это без необходимости «нумерует» все существующие (до тех же значений, которые они уже имели) , Но как любой полностью автоматизированный подход может «узнать», какие элементы, если таковые имеются, он должен пересчитать .bp
из, без O(N)
усилий? По крайней мере, он должен рассматривать каждый из них (поскольку вы не хотите отдельно кодировать, например, append
против insert
& c), и это уже O(N)
.
Так что это будет приемлемо, только если все нормально, если каждое изменение в списке равно O(N)
(в основном, только если список всегда остается маленьким и / или не часто меняется).
Более плодотворная идея может заключаться в том, чтобы не поддерживать значения .bp
все время, а только "вовремя", когда это необходимо. Сделайте bp
свойством (только для чтения), вызывая метод, который проверяет, является ли контейнер «грязным» (где флаг «грязного» в контейнере поддерживается с использованием уже предоставленного мной автоматизированного кода), и только после этого перенумеровывает контейнер (и устанавливает для его атрибута dirty значение False
).
Это будет хорошо работать, когда список, как правило, подвергается серии изменений, и только тогда вам нужно будет некоторое время получить доступ к элементам bp
, затем другой группе изменений и т. Д. Такое бурное чередование между изменениями и чтение не является редкостью в реальных контейнерах, но только вы можете знать, применимо ли оно в вашем конкретном случае!
Чтобы повысить производительность сверх этого, я думаю, вам нужно сделать некоторое ручное кодирование в дополнение к этому общему подходу, чтобы воспользоваться частыми частными случаями. Например, append
можно вызывать очень часто, а объем работы, выполняемой в особых случаях append
, действительно невелик, поэтому вполне возможно, что стоит написать эти две или три строки кода (не установка грязного бита для этого случая).
Одно предостережение: ни один подход не сработает (действительно, ваше требование становится противоречивым), если какой-либо элемент присутствует в списке дважды - что, конечно, вполне возможно, если вы не примете меры предосторожности, чтобы избежать его (вы могли бы легко диагностировать его в 1053 * - сохраняя набор уже увиденных элементов и вызывая исключение при любом дублировании - если вам еще не поздно, диагностировать сложнее «на лету», то есть во время мутации, которая вызывает дублирование , если это то, что вам требуется). Возможно, вы можете ослабить свое требование, чтобы, если элемент присутствовал дважды, это нормально, и bp
может просто указывать один из индексов; или превратить bp
в набор индексов, в которых присутствует элемент (это также обеспечило бы плавный подход к случаю получения bp
из элемента, который не в список). И т. Д .; Я рекомендую вам подробно рассмотреть (и документ !) Все эти угловые случаи - правильность перед исполнением!