class Mark:

    def __init__(self, values):
        self.values = values
        self.index = 0

    def __iter__(self):
        print('Call iter Mark')
        return self

    def __next__(self):
        print('Call next Mark')
        if self.index >= len(self.values):
            raise StopIteration
        value = self.values[self.index]
        self.index += 1
        return value


class Student:

    def __init__(self, name, surname, makrs):
        self.name = name
        self.surname = surname
        self.marks = makrs

    # getitem реализует обращение по индексу
    # Теперь можно итерироваться! Но не только так!
    def __getitem__(self, item):
        return self.name[item]

    # елси нет своего __next__, будет вызван next внутри класса строки
    # __iter__ должен возвращать объект, у которого реализован метод next
    def __iter__(self):
        print('Call iter Student')
        self.index = 0
        return self.marks

    def __next__(self):
        print('Call next Student')
        if self.index >= len(self.name):
            raise StopIteration
        letter = self.name[self.index]
        self.index += 1
        return letter


def test_iter_1():

    mark = Mark([2, 4, 3, 1, 5])
    igor = Student('Igor', 'Nikolaev', mark)

    # будет вызван метод __iter__, а если его нет __getitem__
    for i in igor:
        print(i)


def main():

    test_iter_1()

    return

    igor = Student('Igor', 'Nikolaev', [2, 4, 3, 1, 5])
    
    a = [1, 2, 3]
    b = iter(a)
    
    try:
        print(type(b))
        print(next(b))
        print(next(b))
        print(next(b))
        print(next(b))
    except StopIteration:
        print("Стор, нет следующего элемента!!!")
    
    # А можно сделать так
    c = a.__iter__()
    c.__next__()
    c.__next__()

if __name__ == '__main__':
    main()