个人觉得Godot4.0引入的Callable补全了GDScript可用性的最后一环,它让函数成为了对象,带来了更强的灵活性。从此,在连接信号时不用再写字符串代表函数了,也可以用黑科技构建高阶函数,至此,GDScript的可用性真正与其他脚本语言看齐。 本文将介绍一些使用过程中发掘的Callable用法和神秘特性(和其他语言不同的即迷惑又有道理的表现)。
一、Callable
当一个函数被调用时,并不一定需要一个实例来调用它,比如静态函数: 可以直接用类名进行调用: 但静态函数不能作为Callable赋值给一个对象: 会报错
定义一个简单的类: 可以将成员函数赋值给一个变量: 可以成功调用。
众所周知,临时变量会在离开作用域之后销毁。如果一个Callable是一个临时变量的函数,则在离开作用域后将无法调用,因为它绑定的实例已经被销毁了。 我们构造一个用例: 在运行时,将会报错: 从报错信息中可以看出,调用者被回收了,变成了null,导致函数调用失败。 不知道这是bug还是feature,理论上函数应该保持对实例的引用才对。但想想python函数也压根没有保持对对象的引用,而是通过self传入实例,这么一想也变得合理了。 因此,我们可以保存一下Callable对象的实例,防止其被回收: 这下就能正常运行了。
二、lambda与匿名函数
匿名函数会自动捕捉上下文遇到的变量,加入闭包。
也可以写多行:
之所以单独写这么一条,是因为其他绝大多数语言当lambda只有一个表达式时默认返回这个表达式的值,但GDScript不是这样。它的匿名函数表现和普通函数一样。 如果直接这么写,就会返回null。 要写成: 才会返回表达式。
null
但可以调用静态函数: 当然也可以调用别人的实例函数,只要有对象。
但是可以用一个变量保存self,从而间接调用: 同时,因为函数闭包内保存了对象o的引用,因此我们不必担心对象被销毁导致无法调用的问题。
但匿名函数在实例被销毁后仍然可以调用。推测匿名函数其实不包含实例来调用它,和callable不同。