自定义迭代器
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 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.gd 中编写 Bookshelf 类。它将书名保存在 _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))
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器