Python, как сделать остановку "для итерации" на "len" с помощью методов dunder

У меня есть несколько классов Python, которые я использую для вызова C-кода, используя c-types. Структура возврата выглядит примерно так, как в приведенном ниже примере.

import ctypes

class MyCClass(ctypes.Structure):
    _fields_ = [('n_values', ctypes.c_int),\
                ('values', ctypes.c_double * 5)]

    def __repr__(self):
        return """n_values : {0}, values : {1}""".format(self.n_values,\
                             self.values)

    def __len__(self):
        return self.n_values

    def __getitem__(self, key):
        return self.values[key]

Массив values имеет фиксированный размер, чтобы упростить вызов C (использование массива переменного размера здесь не вариант). «Фактическая» длина массива контролируется переменной n_values.

Например, если values представляет собой массив из трех чисел, скажем, 1, 2 и 3, values=[1, 2, 3, 0, 0] и n_values=3.

Все в порядке. Проблема в том, когда я реализую __len__ и __getitem__.

Я хочу писать такой код

for value in my_class:
    #do something

Но итератор вроде бы не «уловил», что values-массив имеет длину только n_values. Т.е. похоже, что он не использует MyCClass.__len__ для остановки итерации. Вместо этого он, кажется, перебирает всю длину values.

my_class = MyCClass()

my_class.n_values = 3
sample_values = [1, 2, 3]
for i in range(3):
    my_class.values[i] = sample_values[i]

i = 0
for value in my_class:
    print(i)
    i += 1
0
1
2
3
4

я хочу

i = 0
for value in my_class:
    print(i)
    i += 1
0
1
2

Я знаю, что умею писать код

for i in range(my_class):
    # do something with my_class[i]

но я не этого хочу.

Кто-нибудь знает, как это исправить?

определите метод __iter__ в MyCClass.

Reblochon Masque 30.05.2018 08:36

Да, потому что «старый» протокол итератора на основе getitem просто продолжает работать, пока не встретит IndexError. Просто реализуйте протокол итератора, используя __iter__ и генератор, или, возможно, используя собственный класс итератора, реализующий __iter__ (возвращающий себя) и __next__.

juanpa.arrivillaga 30.05.2018 08:39
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

С типами итераторов старой школы единственный способ - поднять IndexError:

def __getitem__(self, key):
    if key >= len(self):
        raise IndexError
    return self.values[key]

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

Другие вопросы по теме