S3 方法查找



在白皮书中引入的 S3 对象系统的核心是,(S3)方法是遵循 GEN.CLS 命名约定的普通函数(以 GEN.default 作为最终后备)。在这个对象系统的初始 R 实现中,这些方法是在调用泛型的环境(和所有封闭环境)中搜索的。

随着命名空间的出现(参见 Tierney (2003),“R 的名称空间管理”,R News,3(1):2-6),通过 NAMESPACE 文件中的 S3method() 指令注册 S3 方法的机制被添加进来。使用 S3method(GEN, CLS)S3method(GEN, CLS, FUN) 分别为函数命名,GEN.CLSFUN 无需导出即可分派。使用后者(3 个参数)指令可以绕过 GEN.CLS 命名约定。注册中使用的泛型是“从包中看到的”,因此需要在加载命名空间时提供。此注册机制适用于所有包(因为它们都有一个命名空间),但不适用于脚本代码(包外部)。

最初,只有在调用环境中找不到合适的方法时,才会查询 S3 注册表。基本方法传统上都遵循 GEN.CLS 命名约定,因此未注册,因为它们可以在 .BaseNamespaceEnv 中找到,作为包命名空间环境的静态部分的最后一个元素,因此位于从全局环境(.GlobalEnv,用户的 workspace)开始并以基本环境(.BaseEnv)结束的搜索路径之前。但是,所有已注册的方法都可以在附加包中被 GEN.CLS 导出(在注册表之前的搜索路径上找到)所掩盖。为了使 S3 查找更加安全和高效,在 R 3.5.0 中进行了更改,以便在调用环境的顶级环境(参见 ?topenv)之后使用注册表。同时,基本中的所有 S3 方法现在也已注册。

自 R 3.6.0 起,NAMESPACE 中的 S3method() 指令也可用于执行延迟 S3 方法注册。使用 S3method(PKG::GEN, CLS, FUN) 函数 FUN 将仅在加载 PKG 的命名空间时才注册为包 PKG 的类 CLS 和泛型 GEN 的 S3 方法。这可用于处理不需要“立即”的情况,并且为了执行立即注册而必须预先加载 pkg 的命名空间(及其所有强依赖项)被认为“代价太大”。

自 trunk 中的 c76951(于 2019-08-10 提交)以来,有一个 .S3method() 函数用于在脚本中注册 S3 方法。同样,这允许以不同于 GEN.CLS 的名称注册方法。

通过所有这些注册增强功能,不再需要通过搜索路径(位于 .GlobalEnv.BaseEnv 之间)查找 S3 方法,因此,trunk 中的 c77043(于 2019-08-19 提交)默认情况下已关闭。目前可以通过一个内部环境变量来控制,该变量最终会消失,因为依赖于此类中间搜索路径查找既不安全又低效。这似乎是一个重大更改:但事实上,CRAN 常规检查已在一年多前关闭了中间搜索路径查找,并且所有源于此更改的检查问题早已消除。类似地,Bioconductor 将在即将发布的 3.10 版中为其软件包添加所有缺少的 S3 方法注册。