Почему срезы с плавающей точкой (slice (0,1,0.1)) разрешены в python, а вызов метода index (slice (0,1,0.1) .indices) вызывает TypeError? - PullRequest
4 голосов
/ 03 апреля 2020

Пример: slice(0,1,0.1) нормально в python 3.7.7, но slice(0,1,0.1).indices(10) поднимает Type Error.

Мне интересно, каков основной мыслительный процесс для этого поведения. Почему нормально создавать срезы с плавающей запятой, но нельзя вызывать метод indices для них? Что такое сценарий использования для indices?

Для контекста: я понимаю сценарий использования для срезов с плавающей запятой - у меня есть один сам. Я реализую объект, который представляет параметризованную кусочно-линейную функцию в произвольных измерениях, определенных конечным набором опорных точек.

1 Ответ

2 голосов
/ 03 апреля 2020

tl; dr slice объекты генерируются интерпретатором, когда мы используем их в квадратных скобках, и разрешение их произвольности позволяет нам разрабатывать код, который их использует. Тем не менее, поскольку встроенный список для правильной работы полагается на indices(), этот метод должен возвращать совместимые со списком значения (т. Е. Целые числа), а если он не может, он выдает ошибку.


Когда вы делаете

my_obj[1:3:2]

, интерпретатор по существу переводит его * как

my_obj.__getitem__(slice(1, 3, 2))

Это наиболее очевидно при использовании списков, которые имеют особое поведение, когда задаются фрагменты, но это поведение также есть другие типы данных в различных популярных библиотеках (например, numpy.array и pandas.Dataframe). Эти классы реализуют свои собственные __getitem__() методы, которые имеют свои собственные особые способы обработки слайсов.

Теперь встроенный список предположительно использует slice.indices() для разложения всего слайса на набор отдельных индексов, которые он может получить доступ, а затем сгруппировать и вернуться. Индексы списков могут быть только целыми числами, и они не хотят, чтобы эта функциональность нарушалась, поэтому наиболее последовательный способ go об этом - заставить slice.indices() выдавать ошибку, когда он не может создать список целых чисел.

Они не могут ограничить slice наличием только этих значений, поскольку это объект, созданный интерпретатором, который могут захотеть использовать другие пользовательские классы. Если вы проектируете объект, подобный этому:

class myGenerator:
    def __getitem__(self, s):  # s is a slice
        def gen():
            i = s.start
            while i < s.stop:
                yield i
                i += s.step
        return list(gen())

h = myGenerator()
print(h[1:4:.25])
# [1, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75]
print(h[0:1:0.1])
# [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999, 0.9999999999999999]

, тогда он может выбрать нотацию среза, чтобы работать так, как ему хочется, поэтому мы можем установить для него пользовательское поведение. Но если бы мы изменили slice.indices(), чтобы использовать это вместо этого, то это сломало бы встроенный list - таким образом, python не позволяет нам.


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

...