создание индекса списка возможным для списка классов python

#python #r #list #function #methods

Вопрос:

В Python нет функции для аргумента индекса списка в __getitem()__ , __setitem()__ , __delitem()__ . Поскольку я долгое время был пользователем R, использование индекса списка выглядит вполне естественным. Я знаю, что есть pandas или numpy , но менять тип громоздко. И ДЛЯ ЭТОГО НУЖЕН ДОПОЛНИТЕЛЬНЫЙ ПАКЕТ!

 >>> import numpy as np
>>> lst = [1,2,3,4,5]
>>> np.array(lst)[[0,3,4]]
array([1, 4, 5])
 

Вот мое предложение.

 class list2(__builtins__.list):
    def __getitem__(self, x):
        if isinstance(x, __builtins__.list):
            #print(x)
            return [__builtins__.list.__getitem__(self, y) for y in x]
        else:
            return __builtins__.list.__getitem__(self,x)
    def __setitem__(self, index, elem):
        if isinstance(index, __builtins__.list):
            if isinstance(elem, __builtins__.list):
                for i,x in zip(index, elem):
                    __builtins__.list.__setitem__(self, i, x)
            else:
                for i in index:
                    __builtins__.list.__setitem__(self, i, elem)
                #self[i] = x
        else:
            __builtins__.list.__setitem__(self, index, elem)
    def __delitem__(self, index):
        if isinstance(index, __builtins__.list):
            for i in sorted(index, reverse = True):
                __builtins__.list.__delitem__(self, i)
                #self[i] = x
        else:
            __builtins__.list.__delitem__(self, index)
 

Кажется, это работает нормально.

 >>> l = list2(['a', 'b', 'c', 'd', 'e'])
>>> l[[2,3]]
['c', 'd']
>>> l[[2,3]]= ['-', '-']
>>> l
['a', 'b', '-', '-', 'e']
>>> del l[[0,3]]
>>> l
['b', '-', 'e']
 

Но есть ли какая-нибудь ловушка? или есть какая-то часть, которую необходимо улучшить?

Ответ №1:

Основная загвоздка с чем — то подобным заключается в том, что это «удивительно», что обычно не одобряется в Python-мы ожидаем, что список по умолчанию будет вести себя как список по умолчанию.

Кроме того, ваша реализация в основном выглядит завершенной для меня. Возможно, есть пара проблем:

  • Что произойдет, если мы используем __setitem__ или __delitem__ и перечислим один и тот же индекс более одного раза?
  • Что произойдет, если мы приведем __setitem__ списки с несоответствующей длиной?

Есть одна ключевая вещь, которую, я думаю, стоило бы изменить: избавьтесь от списков индексов и вместо этого используйте кортежи.

Если вы передадите разделенный запятыми список индексов __getitem__ , они фактически будут проанализированы как кортеж!

 >>> class Thing:
...     def __getitem__(self, indices):
...         print(indices)
...
>>> t = Thing()
>>> t[1, 2, 3]
(1, 2, 3)
 

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

С точки зрения других улучшений, вероятно , лучше избавиться от ссылок __builtins__ и использовать super() для доступа к функциям из реализации списка по умолчанию, и рекомендуется добавить пользовательский __repr__ , чтобы мы не путали это со списком по умолчанию.

Если вы добавите все эти предложения, это будет выглядеть примерно так (ввод добавлен для ясности):

 from typing import Any, Tuple, Union

class list2(list):
    def __getitem__(self, index: Union[int, Tuple[int], slice]) -> Any:
        if isinstance(index, (slice, int)):
            return super().__getitem__(index)

        return [super(list2, self).__getitem__(x) for x in index]

    def __setitem__(self, index: Union[int, Tuple[int], slice], elem: Any):
        if isinstance(index, (slice, int)):
            return super().__setitem__(index, elem)

        if len(index) != len(elem):
            raise ValueError("Number of elements in index does not match element.")

        for x, element in zip(index, elem):
            self[x] = element

    def __delitem__(self, index: Union[int, Tuple[int], slice]):
        if isinstance(index, (slice, int)):
            return super().__delitem__(index)

        for x in sorted(set(index), reverse=True):
            super().__delitem__(x)

    def __repr__(self) -> str:
        return f"list2({super().__repr__()})"

 

Комментарии:

1. Примечание: Это все равно будет работать , например l[[1, 2, 3]] , это просто больше не нужно: l[1, 2, 3] приведет к тому же результату.

2. Отличный ответ! Одна из причин, по которой я выбрал __builtins__.list , заключается в том, что я изменил определение list на что-то вроде def list(*args): if len(args) > 1: return [*args] else: __builtins__.list(args[0]) . В любом случае, кажется, что нет никакого способа ссылаться на родной list , что бы пользователь ни сделал с окружающей средой. __builtins__.list может быть легко назначен на другую функцию.