S4 类: 传递给 new() 的参数没有进入它们的位置
S4 classes: arguments passed to new() don't go into their slots
我正在使用 S4 classes 构建 R 包,但我在使用 new
函数时遇到了问题。我有一个 class 叫做 Configs
setClass("Configs",
slots = list(
burnin = "numeric",
chains = "numeric",
features = "numeric",
iterations = "numeric",
mphtol = "numeric",
samples = "numeric",
seed = "numeric",
thin = "numeric",
verbose = "numeric"
),
prototype = list(
burnin = 0,
chains = 2,
features = 5,
iterations = 5,
mphtol = 1e-4,
samples = 3,
seed = sample(1e6, 1),
thin = 0,
verbose = 0
)
)
当我只将这部分加载到我的全局环境中时,我可以创建一个新的 Configs
对象,其插槽不同于默认值。
> new("Configs", features = 1000)
An object of class "Configs"
Slot "burnin":
[1] 0
Slot "chains":
[1] 2
Slot "features":
[1] 1000
Slot "iterations":
[1] 5
Slot "mphtol":
[1] 1e-04
Slot "samples":
[1] 3
Slot "seed":
[1] 437211
Slot "thin":
[1] 0
Slot "verbose":
[1] 0
但是,当我安装整个包,将其加载到一个新的环境中,然后 运行 new("Configs", features = 1000)
,我得到 features
5。为什么 new()
将值放入槽中了吗?
我的包裹通过了 R CMD check
,没有任何错误、警告或注释。这是我的会话信息。
> sessionInfo()
R version 3.2.0 (2015-04-16)
Platform: x86_64-unknown-linux-gnu (64-bit)
Running under: CentOS release 6.6 (Final)
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] heterosis_0.0 pracma_1.8.3 MCMCpack_1.3-3 MASS_7.3-40 coda_0.17-1
loaded via a namespace (and not attached):
[1] tools_3.2.0 grid_3.2.0 lattice_0.20-31
编辑:明白了,但还是不满意
原来是我的 initialize
函数出了问题。
setMethod("initialize", "Configs", function(.Object, ...){
# .Object = new("Configs", ...)
validObject(.Object)
return(.Object)
})
当我删除它时,new
再次将东西放入槽中。我很高兴我发现了问题,但我不想完全删除我的初始化函数。我想要一种方便的方法来调用 validObject 并进行其他错误检查,initialize
似乎是一个合适的地方。如果我取消注释注释行,我会得到一个无限递归。如何在不破坏 new
的情况下创建构造函数?
initialize()
具有双重用途——初始化和复制构造。提供一个明确的构造函数通常更好(也给用户提供更多信息)
.A = setClass("A", representation(x="numeric"))
A = function(x=numeric(), ...)
.A(x=x, ...)
validOjbect()
当对象创建涉及槽赋值时,默认会调用 initialize 方法,因此无需在您自己的 initialize 方法中显式调用它(见下文);也许你会
.A = setClass("A", representation(x="numeric"),
prototype=prototype(x=NA_integer_))
setValidity("A", function(object) {
if (length(object@x) != 1L)
"'x' must be length 1"
else TRUE
})
A = function(x=NA_integer_, ...)
## signature is informative -- 'x' is integer(1), not just '...'
## coercion (e.g., as.integer(), below) and other set-up
new("A", x=as.integer(x), ...)
与
> A()
An object of class "A"
Slot "x":
[1] NA
> A(x=1)
An object of class "A"
Slot "x":
[1] 1
> A(x=1:2)
Error in validObject(.Object) :
invalid class "A" object: 'x' must be length 1
一个重要的警告是,当没有用户初始化的插槽时,不调用有效性方法,因此必须定义prototype()
来创建一个有效对象(用 validObject(new("A"))
.
验证
对于你的问题,有效性函数是正确的地方'other error checking'。编写正确的初始化方法非常困难,但更接近正确的是
.B = setClass("B",
representation(x="numeric", y="numeric"),
prototype=prototype(x=NA_integer_, y=NA_real_))
setMethod("initialize", "B",
function(.Object, ..., x=.Object@x, y=.Object@y)
{
## pre-processing, then invoke 'next' initialize() method
## base initialize() creates the object then calls validObject()
## so no need for explicit test of validity
.Object <- callNextMethod(.Object, ..., x=x, y=y)
## post-processing
.Object
})
这种奇怪的构造允许 initialize()
继续充当复制构造函数
> b = new("B", x=1, y=2) # constructor
> initialize(b, x=2) # copy-constructor
An object of class "B"
Slot "x":
[1] 2
Slot "y":
[1] 2
这在 class 继承过程中很重要。但是正如您所看到的,这是非常棘手的——最终它真的很难而且很少值得付出努力来 initialize()
正确。
请注意,我们尚未完全履行 initialize()
、
的合同
setClass("C", representation(x="numeric", y="numeric")) # default initialize()
当用 new()
调用时,它实际上充当复制构造函数
> c = new("C", x=1, y=2)
> new("C", c, x=2)
An object of class "C"
Slot "x":
[1] 2
Slot "y":
[1] 2
相对于 B 的实现没有复制构造
> b = new("B", x=1, y=2)
> new("B", b, x=2)
An object of class "B"
Slot "x":
[1] 2
Slot "y":
[1] NA
我正在使用 S4 classes 构建 R 包,但我在使用 new
函数时遇到了问题。我有一个 class 叫做 Configs
setClass("Configs",
slots = list(
burnin = "numeric",
chains = "numeric",
features = "numeric",
iterations = "numeric",
mphtol = "numeric",
samples = "numeric",
seed = "numeric",
thin = "numeric",
verbose = "numeric"
),
prototype = list(
burnin = 0,
chains = 2,
features = 5,
iterations = 5,
mphtol = 1e-4,
samples = 3,
seed = sample(1e6, 1),
thin = 0,
verbose = 0
)
)
当我只将这部分加载到我的全局环境中时,我可以创建一个新的 Configs
对象,其插槽不同于默认值。
> new("Configs", features = 1000)
An object of class "Configs"
Slot "burnin":
[1] 0
Slot "chains":
[1] 2
Slot "features":
[1] 1000
Slot "iterations":
[1] 5
Slot "mphtol":
[1] 1e-04
Slot "samples":
[1] 3
Slot "seed":
[1] 437211
Slot "thin":
[1] 0
Slot "verbose":
[1] 0
但是,当我安装整个包,将其加载到一个新的环境中,然后 运行 new("Configs", features = 1000)
,我得到 features
5。为什么 new()
将值放入槽中了吗?
我的包裹通过了 R CMD check
,没有任何错误、警告或注释。这是我的会话信息。
> sessionInfo()
R version 3.2.0 (2015-04-16)
Platform: x86_64-unknown-linux-gnu (64-bit)
Running under: CentOS release 6.6 (Final)
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] heterosis_0.0 pracma_1.8.3 MCMCpack_1.3-3 MASS_7.3-40 coda_0.17-1
loaded via a namespace (and not attached):
[1] tools_3.2.0 grid_3.2.0 lattice_0.20-31
编辑:明白了,但还是不满意
原来是我的 initialize
函数出了问题。
setMethod("initialize", "Configs", function(.Object, ...){
# .Object = new("Configs", ...)
validObject(.Object)
return(.Object)
})
当我删除它时,new
再次将东西放入槽中。我很高兴我发现了问题,但我不想完全删除我的初始化函数。我想要一种方便的方法来调用 validObject 并进行其他错误检查,initialize
似乎是一个合适的地方。如果我取消注释注释行,我会得到一个无限递归。如何在不破坏 new
的情况下创建构造函数?
initialize()
具有双重用途——初始化和复制构造。提供一个明确的构造函数通常更好(也给用户提供更多信息)
.A = setClass("A", representation(x="numeric"))
A = function(x=numeric(), ...)
.A(x=x, ...)
validOjbect()
当对象创建涉及槽赋值时,默认会调用 initialize 方法,因此无需在您自己的 initialize 方法中显式调用它(见下文);也许你会
.A = setClass("A", representation(x="numeric"),
prototype=prototype(x=NA_integer_))
setValidity("A", function(object) {
if (length(object@x) != 1L)
"'x' must be length 1"
else TRUE
})
A = function(x=NA_integer_, ...)
## signature is informative -- 'x' is integer(1), not just '...'
## coercion (e.g., as.integer(), below) and other set-up
new("A", x=as.integer(x), ...)
与
> A()
An object of class "A"
Slot "x":
[1] NA
> A(x=1)
An object of class "A"
Slot "x":
[1] 1
> A(x=1:2)
Error in validObject(.Object) :
invalid class "A" object: 'x' must be length 1
一个重要的警告是,当没有用户初始化的插槽时,不调用有效性方法,因此必须定义prototype()
来创建一个有效对象(用 validObject(new("A"))
.
对于你的问题,有效性函数是正确的地方'other error checking'。编写正确的初始化方法非常困难,但更接近正确的是
.B = setClass("B",
representation(x="numeric", y="numeric"),
prototype=prototype(x=NA_integer_, y=NA_real_))
setMethod("initialize", "B",
function(.Object, ..., x=.Object@x, y=.Object@y)
{
## pre-processing, then invoke 'next' initialize() method
## base initialize() creates the object then calls validObject()
## so no need for explicit test of validity
.Object <- callNextMethod(.Object, ..., x=x, y=y)
## post-processing
.Object
})
这种奇怪的构造允许 initialize()
继续充当复制构造函数
> b = new("B", x=1, y=2) # constructor
> initialize(b, x=2) # copy-constructor
An object of class "B"
Slot "x":
[1] 2
Slot "y":
[1] 2
这在 class 继承过程中很重要。但是正如您所看到的,这是非常棘手的——最终它真的很难而且很少值得付出努力来 initialize()
正确。
请注意,我们尚未完全履行 initialize()
、
setClass("C", representation(x="numeric", y="numeric")) # default initialize()
当用 new()
> c = new("C", x=1, y=2)
> new("C", c, x=2)
An object of class "C"
Slot "x":
[1] 2
Slot "y":
[1] 2
相对于 B 的实现没有复制构造
> b = new("B", x=1, y=2)
> new("B", b, x=2)
An object of class "B"
Slot "x":
[1] 2
Slot "y":
[1] NA