Пользовательская оболочка для индексации списка Python, начиная с 1 - PullRequest
3 голосов
/ 16 июля 2011

Я хотел бы написать простую оболочку для типа python list, которая заставляет его начать индексирование с 1 вместо 0.У меня есть довольно сложная программа, основанная на некоторых дискретных распределениях вероятности данных продолжительности, с целочисленными сегментами, но у меня нет длительностей меньше 1. В любом случае, это значительно упростит некоторые существенные части моего кодачтобы можно было плавно индексировать, начиная с 1. Сначала я использовал dict, но обнаружил, что некоторые их свойства слишком громоздки.

Я никогда раньше не писал оболочку для класса Python,гораздо менее встроенный тип, но я чувствую, что то, что я хочу сделать, довольно просто.Как минимум, я должен быть в состоянии сделать что-то вроде этого:

>>> p = one_list([1,2,3,4,5])
>>> for i in range(1,6):
    print i, p[i]

1 1
2 2
3 3
4 4
5 5
>>> len(p)
5

Однако было бы хорошо, если бы я мог перезаписать некоторые другие соответствующие встроенные методы класса listНапример, index.

>>> len(p)
5
>>> p.index(p[-1])
5

Пожалуйста, поделитесь своими советами о том, как я могу поступить так.Я размышлял, стоит ли просто делать это с пользовательским классом, но кажется, что это может быть излишним.Я также приветствую любые рекомендации относительно полезных методов перезаписи.

Редактировать: Послесловие

Я просто хотел бы отметить, что делать это не стоити причина, по которой я принял ответ ниже, не в том, что я пытался реализовать его так, как он описывает, а в том, что он помог мне понять, что списки сами по себе достаточно хороши.

Ответы [ 3 ]

3 голосов
/ 16 июля 2011

Вот полная (я думаю) реализация списка, основанного на 1, правильная обработка срезов (включая расширенные срезы), индексация, выталкивание и т. Д. Это немного сложнее, чем вы думаете, особенно нарезка и отрицательные показатели. На самом деле я все еще не уверен на 100%, что он работает точно так, как должно, поэтому будьте осторожны.

class list1(list):
    """One-based version of list."""

    def _zerobased(self, i):
        if type(i) is slice:
            return slice(self._zerobased(i.start),
                         self._zerobased(i.stop), i.step)
        else:
            if i is None or i < 0:
                return i
            elif not i:
                raise IndexError("element 0 does not exist in 1-based list")
            return i - 1

    def __getitem__(self, i):
        return list.__getitem__(self, self._zerobased(i))

    def __setitem__(self, i, value):
        list.__setitem__(self, self._zerobased(i), value)

    def __delitem__(self, i):
        list.__delitem__(self, self._zerobased(i))

    def __getslice__(self, i, j):
        print i,j
        return list.__getslice__(self, self._zerobased(i or 1),
                                 self._zerobased(j))

    def __setslice__(self, i, j, value):
        list.__setslice__(self, self._zerobased(i or 1),
                          self._zerobased(j), value)

    def index(self, value, start=1, stop=-1):
        return list.index(self, value, self._zerobased(start),
                          self._zerobased(stop)) + 1

    def pop(self, i):
        return list.pop(self, self._zerobased(i))

senderle ExtraItemList будет иметь лучшую производительность, потому что ему не нужно постоянно корректировать индексы, и при этом у него нет дополнительного уровня вызовов (не C!) Между вами и данными. Жаль, что я не думал об этом подходе; возможно я мог бы выгодно объединить это с моим ...

1 голос
/ 16 июля 2011

Вот базовая реализация, с которой можно начать.Я подумал, что было бы неплохо обобщить это, чтобы вы могли иметь списки с индексами, начинающимися с любого целого числа, которое вам нравится.Это привело меня к открытию __slots__, поэтому спасибо за вопрос!

class OffsetList(list):
    __slots__ = 'offset'
    def __init__(self, init=[], offset=-1):
        super(OffsetList, self).__init__(init)
        self.offset = offset
    def __getitem__(self, key):
        return super(OffsetList, self).__getitem__(key + self.offset)
    def __setitem__(self, key, value):
        return super(OffsetList, self).__setitem__(key + self.offset, value)
    def __delitem__(self, key):
        return super(OffsetList, self).__delitem__(key + self.offset)
    def index(self, *args):
        return super(OffsetList, self).index(*args) - self.offset

>>> a = OffsetList([1,2,3])
>>> a
[1, 2, 3]
>>> a[1]
1
>>> a.index(2)
2

Возможно, вы захотите реализовать __getslice__, __setslice__, __delslice__тоже не говоря уже о pop и insert.

1 голос
/ 16 июля 2011

Хорошо, ты выглядишь очень решительным.Как я уже говорил выше, вы должны переопределить __getitem__, __setitem__ и __delitem__.На самом деле вам также придется переопределить __getslice__, __setslice__, __delslice__ (в противном случае срезы начинаются с 0, как обычно).Затем __add__, __mul__, __iadd__, __imul__ также вернут WonkyList s (иначе конкатенация не будет работать так, как вы хотите).Вам придется переопределить index, потому что он вернет неправильное значение (я проверял это).Также insert, remove и pop.Вот кое-что, с чего можно начать:

class WonkyList(list):
    def __getitem__(self, index):
        return super(WonkyList, self).__getitem__(index - 1)
    def __setitem__(self, index, val):
        super(WonkyList, self).__setitem__(index - 1, val)
    def __delitem__(self, index):
        super(WonkyList, self).__delitem__(index - 1)

Протестировано:

>>> w = WonkyList(range(10))
>>> w[1]
0
>>> del w[5]
>>> w
[0, 1, 2, 3, 5, 6, 7, 8, 9]
>>> w[1] = 10
>>> w
[10, 1, 2, 3, 5, 6, 7, 8, 9]

Вот несколько способов, которые могут привести к сбою, пока вы не переопределите все необходимые методы:

>>> w
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> w[5]
4
>>> w.pop(5)
5
>>> w.insert(5, 5)
>>> w
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del w[2:4]
>>> w
[0, 1, 4, 5, 6, 7, 8, 9]
>>> 
>>> w.index(1)
1

Или вы можете попробовать что-нибудь еще.Рассматривали ли вы, например, ...

def ExtraItemList(list):
    def __init__(self, seq):
        self[0] = 0
        self[1:] = seq
    def n_items(self):
        return len(self) - 1

Это, кажется, решает проблемы, которые вы перечисляете, и все еще ведет себя в основном как список, даже если инициализатор немного странный.Хотя, честно говоря, глядя на приведенные вами примеры, я начинаю чувствовать, что даже это уродливое решение - ни одна из функций, которые вы дали, не демонстрирует преимущества, начиная с индексации с 1, они просто показывают плохие эффекты добавлениядополнительный пункт в начале списка.Основываясь на этих примерах, вы должны просто использовать обычный список.

Возможно, лучшим подходом будет написать пользовательские методы get и set, которые используют желаемое смещение.Таким образом списки будут вести себя нормально, но при необходимости у вас будет альтернативный интерфейс.

...