我有一个R包(当前不在CRAN上),它从其他包(特别是knitr::knit_print
和huxtable::as_huxtable
)定义了几种通用函数的S3方法。但是,它们不是我的软件包的关键部分,因此我宁愿在用户安装我的软件包时也不对这些软件包创建依赖关系。直到R 4.0.0,我没有导入泛型就导出了S3方法。使用roxygen2
,将我的@export
指令转换为NAMESPACE中的export()
指令,而不是S3method()
。在R版本<4.0.0中,这很好用,因为R首先在全局环境中寻找匹配的generic_function.class
方法,而不是依赖于S3方法的正确注册。但是,根据this blog on developer.r-project.org,R不再寻找未注册的S3方法。
最好的解决方法是什么?现在,我已经将@importFrom
指令添加到了roxygen2
块中,并将这两个包都添加到了description的imports部分。但是,据我所知,这意味着任何安装我的软件包的用户都必须安装knitr
和huxtable
,无论他们是否愿意。
幸运的是,对于R> = 3.6.0,您甚至不需要the answer by caldwellst。在上面链接的博客条目中:
自R 3.6.0起,NAMESPACE中的S3method()指令还可用于执行延迟的S3方法注册。使用S3method(PKG :: GEN,CLS,FUN)函数,只有在加载PKG的命名空间时,FUN才会从包PKG中注册为CLS类和通用GEN的S3方法。可以使用它来处理不需要“立即”使用该方法的情况,并且为了执行立即注册而必须预先加载pkg的名称空间(及其所有强依赖性)被认为过于“昂贵”。
此外,文档中还讨论了vctrs::s3_register()
的其他建议:
#' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating
#' class creation in a vignette, since method lookup no longer always involves
#' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect
#' by using "delayed method registration", i.e. placing the following in your
#' `NAMESPACE` file:
#'
#' ```
#' if (getRversion() >= "3.6.0") {
#' S3method(package::generic, class)
#' }
因此,您只需要not使用@importFrom
,而不是@export
,请使用@exportS3Method package::generic
(请参阅https://github.com/r-lib/roxygen2/issues/796和https://github.com/r-lib/roxygen2/commit/843432ddc05bc2dabc9b5b22c1ae7de507a00508)
因此,为了说明,我们可以制作两个very简单包,即foo
和bar
。包foo
仅具有通用foo()
函数和默认方法:
library(devtools)
create_package("foo")
#' foo generic
#'
#' @param x An object
#' @param ... Arguments passed to or from other methods
#' @export
foo <- function(x, ...) {
UseMethod("foo", x)
}
#' foo default method
#'
#' @param x An object
#' @param ... Arguments passed to or from other methods
#' @export
foo.default <- function(x, ...) {
print("Called default method for foo.")
}
在document()
和install()
之后,我们创建bar
:
create_package("bar")
为bar
创建一个foo()
方法:
#' bar method for foo
#'
#' @param x A bar object
#' @param ... Arguments passed to or from other methods
#'
#' @exportS3Method foo::foo
foo.bar <- function(x, ...) {
print("Called bar method for foo.")
}
重要的是,在运行foo
之前,我们必须 负载 document()
程序包,否则@exportS3Method
将不起作用。也就是说,
library(foo)
document()
但是,如果这样做,则会在NAMESPACE
中获得bar
的以下内容:
# Generated by roxygen2: do not edit by hand
S3method(foo::foo,bar)
我们必须手动将foo
添加到DESCRIPTION
中的“建议”。
然后,如果我们卸载foo
,我们仍然可以安装bar
:
> remove.packages("foo")
Removing package from ‘/home/duckmayr/R/x86_64-pc-linux-gnu-library/4.0’
(as ‘lib’ is unspecified)
> install("bar")
✓ checking for file ‘/home/jb/bar/DESCRIPTION’ ...
─ preparing ‘bar’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘bar_0.0.0.9000.tar.gz’
Running /opt/R/4.0.0/lib/R/bin/R CMD INSTALL \
/tmp/Rtmp5Xgwqf/bar_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/jb/R/x86_64-pc-linux-gnu-library/4.0’
* installing *source* package ‘bar’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (bar)
vctrs
包提供了一个称为s3_register
的函数,该函数动态注册要在.onLoad
函数中使用的方法。您可以自己了解更多有关here的用法:
.onLoad <- function(...) {
if (requireNamespace("knitr", quietly = TRUE)) {
vctrs::s3_register("knitr::knit_print", "class_name")
}
if (requireNamespace("huxtable", quietly = TRUE)) {
vctrs::s3_register("huxtable::as_huxtable", "class_name")
}
}
该文档也很友好,因此您不必导入vctrs
:
为了避免对该函数依赖vctrs,请随时将函数源复制并粘贴到您自己的程序包中。