Пользовательские итераторы
Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 33 из 65.
Python позволяет классу вести себя как список с помощью магических методов, таких как __len__, __getitem__, __contains__ и __iter__. В Godot есть виртуальные методы только для одной из этих задач — цикла for. Три метода обеспечивают работу for item in my_object::
_iter_init(iter)выполняется один раз перед первым проходом. Он задаёт начальную позицию и возвращаетtrue, если есть первый элемент._iter_get(position)возвращает элемент в текущей позиции, и переменная цикла получает это значение._iter_next(iter)выполняется перед каждым следующим проходом. Он перемещает на следующую позицию и возвращаетfalse, когда элементов больше нет, завершая цикл.
# team.gd
class_name Team
extends RefCounted
var _members: Array[String] = []
func add(member: String) -> void:
_members.append(member)
func _iter_init(iter: Array) -> bool:
iter[0] = 0
return iter[0] < _members.size()
func _iter_next(iter: Array) -> bool:
iter[0] += 1
return iter[0] < _members.size()
func _iter_get(position):
return _members[position]iter — это массив с одним элементом, который Godot создаёт для каждого цикла. iter[0] хранит позицию этого цикла, и Godot передаёт её значение в _iter_get(). Теперь Team работает в цикле for:
var team := Team.new()
team.add("Ada")
team.add("Bo")
for member in team:
print(member) # Ada, затем BoХраните позицию в iter[0], а не в переменной-члене, такой как _position. Каждый цикл получает собственный iter, поэтому два цикла по одной и той же команде, даже если один вложен в другой, не мешают друг другу. При использовании переменной-члена внутренний цикл переместил бы позицию внешнего цикла.
Для остальных операций со списком нет виртуального метода: len(team) и team[0] вызывают ошибки. Напиши обычные методы с такими же именами, как у Array, например size(), get_at(index) и has(member). Не проверяй наличие элемента с помощью in: "Ada" in team проверяет, есть ли у Team свойство или метод с именем Ada, поэтому результат — false.
Задание
ЛегкоНапиши класс Bookshelf в файле bookshelf.gd. Он хранит названия в _titles и может содержать не более _capacity книг:
add(title)добавляет название и возвращаетtrueлибо возвращаетfalse, не добавляя его, если полка заполнена или такое название уже есть на полке.size()возвращает количество книг, аhas(title)— есть ли такое название на полке.get_at(index)возвращает название по этому индексу или пустую строку, если индекс находится за пределами полки._iter_init(),_iter_next()и_iter_get()позволяют циклуforперебирать названия в порядке их добавления.
Предоставленный код считывает вместимость, количество названий и по одному названию в каждой строке, а затем сообщает, какие названия не удалось добавить. Он выводит количество книг, перечисляет их в цикле for по полке, затем считывает ещё одно название и выводит, есть ли оно на полке, а также последнюю книгу. Для вместимости 3, названий Dune, Emma, Dune, Ulysses, Hamlet и поиска Emma он выводит:
Could not add: Dune
Could not add: Hamlet
Books: 3
1. Dune
2. Emma
3. Ulysses
Has Emma: true
Last: UlyssesТвой код должен быть в файле bookshelf.gd. В main.gd находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
extends Node
func _ready():
# Предоставленный код ввода-вывода: оставьте его без изменений
var shelf := Bookshelf.new(int(OS.read_string_from_stdin()))
var count := int(OS.read_string_from_stdin())
for i in count:
var title := OS.read_string_from_stdin()
if not shelf.add(title):
print("Could not add: " + title)
print("Books: %d" % shelf.size())
var number := 1
for title in shelf:
print("%d. %s" % [number, title])
number += 1
var query := OS.read_string_from_stdin()
print("Has %s: %s" % [query, shelf.has(query)])
print("Last: " + shelf.get_at(shelf.size() - 1))
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово selfМетодыСвойстваКонструктор (_init)Повторение: простой калькулятор4Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур7Сигналы
Что такое сигнал?Объявление и отправкаПодключение к сигналамСигналы с аргументамиПовторение — система здоровья10Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»2Свойства классов
Переменные экземпляра и классаСтатические функцииСеттеры свойствГеттеры свойствПриватность по соглашениюПовторение — менеджер банковского счёта5Инкапсуляция
Открытые и закрытые членыПроверка данных с помощью сеттеровСокрытие информацииСвойства только для чтенияПовторение — система учета студентов8Продвинутые концепции ООП
Композиция и наследованиеВнутренние классыУзлы как компонентыRefCounted и NodeПеречисления в классах11Проект: Управление библиотекой
Обзор проектаКлассы книг и пользователей3Наследование
Основы наследованияКлючевое слово superПереопределение методовЦепочки наследованияПроверка типов с помощью isПовторение — иерархия сотрудников6Специальные методы
Введение в виртуальные методыМетод _to_stringПользовательские итераторыПовторение: пользовательский список9Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор GDScript