如何将自定义R包数据设为S4类槽默认值?遇报错求解决方案
问题描述
开发R包时创建了数据集my_pkg_data,通过usethis::use_data(my_pkg_data, internal=FALSE)将其设为外部可访问的包数据。该数据能正常作为函数参数的默认值,但在使用setClass定义S4类时,若在prototype中指定some_slot = test::my_pkg_data作为槽的默认值,运行devtools::check()会报错:'my_pkg_data' is not an exported object from 'namespace:test'。而通过setMethod定义initialize方法设置默认值则可正常运行。需要明确:
- 能否直接在prototype中设置包数据作为默认值?
- 是否必须使用initialize方法?
- 报错的根本原因是什么?
相关代码与配置
创建包数据的代码
my_pkg_data <- sample(100) usethis::use_data(my_pkg_data, internal=FALSE)
可正常运行的函数代码
#' Title #' #' @param x foo #' #' @return nout #' @export #' foo <- function(x = test::my_pkg_data) { print(x) } # 运行正常 foo() # 输出示例:[1] 407 184 13 994 933 322 214 784 502 396 903 ....
报错的S4类定义代码
#' Title #' #' @slot some_slot ... #' @return a s4class object #' @export #' s4class <- setClass( Class = "s4class", slots = list( some_slot = "ANY" ), prototype = list( some_slot = test::my_pkg_data ) )
报错信息
* installing *source* package ‘test’ ... ** using staged installation ** R ** data *** moving datasets to lazyload DB ** byte-compile and prepare package for lazy loading Error : 'my_pkg_data' is not an exported object from 'namespace:test' Error: unable to load R code in package ‘test’ Execution halted ERROR: lazy loading failed for package ‘test’ * removing ‘/private/var/folders/62/n2yrhw752hn73nlbzp0r0_rr0000gq/T/RtmpvHbEA2/file36f3594c2270/test.Rcheck/test’ 1 error ✖ | 0 warnings ✔ | 0 notes ✔
DESCRIPTION文件内容
Package: test Title: What the Package Does (One Line, Title Case) Version: 0.0.0.9000 Authors@R: person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), comment = c(ORCID = "YOUR-ORCID-ID")) Description: What the package does (one paragraph). License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.3 Depends: R (>= 2.10) LazyData: true
NAMESPACE文件内容
# Generated by roxygen2: do not edit by hand export(foo) export(s4class) exportClasses(s4class)
data.R文件内容
#' @title my data #' @name my_pkg_data "my_pkg_data"
可正常运行的initialize方法代码
setMethod( f = "initialize", signature = "s4class", definition = function(.Object, some_slot=test::my_pkg_data) { .Object@some_slot <- some_slot validObject(.Object) return(.Object) } )
问题解答
报错原因
核心是包数据加载时机与S4类定义执行时机不匹配:
- 你的包设置了
LazyData: true,这意味着包数据是延迟加载的——只有当代码实际调用到数据时,才会加载到命名空间中; setClass在包的懒加载阶段就会执行,此时my_pkg_data还未被加载,命名空间里找不到这个对象,因此触发报错;- 函数参数默认值是惰性求值的,只有当函数被调用时才会解析
test::my_pkg_data,此时包数据已经完成加载,所以能正常工作; initialize方法的参数默认值同样遵循惰性求值规则,只有创建类实例时才会触发解析,此时数据已加载,因此不会报错。
是否可以直接在prototype中设置?
理论上可以,但需要修改包的加载配置,且不推荐:
- 将
DESCRIPTION中的LazyData改为false,让包数据在初始化时直接加载到命名空间; - 或者在类定义的R文件开头,手动强制加载数据:
data(my_pkg_data, package = "test")。
这两种方式都会破坏延迟加载的优势,要么增加包的启动时间,要么可能导致重复加载问题,因此不建议使用。
是否必须使用initialize方法?
不是必须,但这是最符合R包开发规范的最优解:
initialize方法利用惰性求值特性,完美避开了包加载阶段的时机冲突,同时保留了延迟加载的优势;- 你也可以在
prototype中设置占位符,再通过自定义构造函数替换为目标数据,但这种方式代码冗余,不如initialize方法简洁规范。
内容的提问来源于stack exchange,提问作者Nicholas Sunderland
相关产品推荐
相关产品推荐

