У меня есть несколько классов 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]
но я не этого хочу.
Кто-нибудь знает, как это исправить?
Да, потому что «старый» протокол итератора на основе getitem просто продолжает работать, пока не встретит IndexError. Просто реализуйте протокол итератора, используя __iter__ и генератор, или, возможно, используя собственный класс итератора, реализующий __iter__ (возвращающий себя) и __next__.






С типами итераторов старой школы единственный способ - поднять IndexError:
def __getitem__(self, key):
if key >= len(self):
raise IndexError
return self.values[key]
Для более чистого решения рассмотрите возможность использования более современного протокола итераций, то есть возврата экземпляра итератор из метода __iter__, определенного в вашем повторяемый. Это задокументировано здесь.
определите метод
__iter__в MyCClass.