S4 class: 在不返回对象的情况下在另一个方法中更新槽 (R)
S4 class: update slot in another method without returning the object (R)
对于我正在处理的 R 包(它创建了一个 S4 class),我想延迟加载一些数据,直到用户实际要求它(因为它可能需要也可能不需要并且加载这需要一点时间)。这将要求我在其 getter (又名访问器)方法中设置一个插槽的值,如果它以前没有被加载的话。但是我无法获得 "stick" 的新值。这是一个 MRE:
setClass("A", representation(value = "numeric"))
setGeneric("value<-", function(x, value) standardGeneric("value<-"))
setGeneric("value", function(x) standardGeneric("value"))
setMethod("value<-", signature("A", "numeric"),
function(x, value)
{
x@value = value
x
})
setMethod("value", signature(x = "A"),
function(x)
{
if(!length(x@value))
value(x) <- 20
x@value
})
这会产生以下结果:
> a <- new("A")
> value(a)
[1] 20
> a
An object of class "A"
Slot "value":
numeric(0)
因此 value() 函数 return 提供了所需的新值 (20),但该值实际上并未在对象中更新。在 getter 中执行 x@value <- value
而不是 value(x) <- 20
也没有成功。
看来问题是我没有 return 在我的 getter 中更新对象(我的 setter 的方式),但我还有其他事情要 return 在我的 getter (值)中。
什么是正确的做法™?
谢谢!
编辑:
在进一步研究 S4 传值语义后,我得出的结论是这是 Simply Not Possible™。如果插槽已更新,则必须 return 编辑对象,而不能 return 其他内容。有人可以确认我的结论是正确的吗?
@Alexis 的评论提到了 R6 类。当我被要求为我当前的项目(针对 BioConductor)进行 S4 类 时,R6 ReferenceClasses 文档中的以下句子引起了我的注意:'Reference classes are implemented as S4 classes with a data part of type "environment".'
因此,如果我在使用 S4 类 时真的想要可变元素,我可以按如下方式模拟 R6:
setClass("A", representation(inMutable = "ANY",
.env = "environment"))
A = function(inMutable = NULL, mutable = NULL) {
x <- new("A",
inMutable = inMutable,
.env = new.env())
x@.env$mutable = mutable
x@inMutable <- inMutable
x
}
setGeneric("inMutable<-", function(x, value) standardGeneric("inMutable<-"))
setGeneric("inMutable", function(x) standardGeneric("inMutable"))
setGeneric("mutable<-", function(x, value) standardGeneric("mutable<-"))
setGeneric("mutable", function(x) standardGeneric("mutable"))
#setter
setMethod("inMutable<-", signature("A", "numeric"),
function(x, value)
{
x@inMutable <- value
x
})
#getter
setMethod("inMutable", signature("A"),
function(x)
{
if (!length(x@inMutable))
message("Hmmm, you haven't initialized a value for 'inMutable'.",
" I'm afraid I can't do anything about that now.")
x@inMutable
})
#setter
setMethod("mutable<-", signature("A", "numeric"),
function(x, value)
{
x@.env$mutable <- value
x
})
#getter (mutable!)
setMethod("mutable", signature("A"),
function(x)
{
if (!length(x@.env$mutable)) {
message("Ah. You haven't initialized a value for 'mutable'. ",
"Lucky for you I can initialize it for you now.")
x@.env$mutable = 12
}
x@.env$mutable
})
那我可以这样做:
> a <- A()
> mutable(a)
Ah. You haven't initialized a value for 'mutable'.
Lucky for you I can initialize it for you now.
[1] 12
> mutable(a)
[1] 12
>
> inMutable(a)
Hmmm, you haven't initialized a value for 'inMutable'.
I'm afraid I can't do anything about that now.
NULL
> inMutable(a) <- 18
> inMutable(a)
[1] 18
>
显然,参考文献 类 和 R6 提供了更丰富、更强大的解决方案,但在紧要关头,这似乎是一个可行的 S4 选项。当然,我还没有在野外彻底测试它,看看它可能在哪里崩溃。
对于我正在处理的 R 包(它创建了一个 S4 class),我想延迟加载一些数据,直到用户实际要求它(因为它可能需要也可能不需要并且加载这需要一点时间)。这将要求我在其 getter (又名访问器)方法中设置一个插槽的值,如果它以前没有被加载的话。但是我无法获得 "stick" 的新值。这是一个 MRE:
setClass("A", representation(value = "numeric"))
setGeneric("value<-", function(x, value) standardGeneric("value<-"))
setGeneric("value", function(x) standardGeneric("value"))
setMethod("value<-", signature("A", "numeric"),
function(x, value)
{
x@value = value
x
})
setMethod("value", signature(x = "A"),
function(x)
{
if(!length(x@value))
value(x) <- 20
x@value
})
这会产生以下结果:
> a <- new("A")
> value(a)
[1] 20
> a
An object of class "A"
Slot "value":
numeric(0)
因此 value() 函数 return 提供了所需的新值 (20),但该值实际上并未在对象中更新。在 getter 中执行 x@value <- value
而不是 value(x) <- 20
也没有成功。
看来问题是我没有 return 在我的 getter 中更新对象(我的 setter 的方式),但我还有其他事情要 return 在我的 getter (值)中。
什么是正确的做法™?
谢谢!
编辑:
在进一步研究 S4 传值语义后,我得出的结论是这是 Simply Not Possible™。如果插槽已更新,则必须 return 编辑对象,而不能 return 其他内容。有人可以确认我的结论是正确的吗?
@Alexis 的评论提到了 R6 类。当我被要求为我当前的项目(针对 BioConductor)进行 S4 类 时,R6 ReferenceClasses 文档中的以下句子引起了我的注意:'Reference classes are implemented as S4 classes with a data part of type "environment".'
因此,如果我在使用 S4 类 时真的想要可变元素,我可以按如下方式模拟 R6:
setClass("A", representation(inMutable = "ANY",
.env = "environment"))
A = function(inMutable = NULL, mutable = NULL) {
x <- new("A",
inMutable = inMutable,
.env = new.env())
x@.env$mutable = mutable
x@inMutable <- inMutable
x
}
setGeneric("inMutable<-", function(x, value) standardGeneric("inMutable<-"))
setGeneric("inMutable", function(x) standardGeneric("inMutable"))
setGeneric("mutable<-", function(x, value) standardGeneric("mutable<-"))
setGeneric("mutable", function(x) standardGeneric("mutable"))
#setter
setMethod("inMutable<-", signature("A", "numeric"),
function(x, value)
{
x@inMutable <- value
x
})
#getter
setMethod("inMutable", signature("A"),
function(x)
{
if (!length(x@inMutable))
message("Hmmm, you haven't initialized a value for 'inMutable'.",
" I'm afraid I can't do anything about that now.")
x@inMutable
})
#setter
setMethod("mutable<-", signature("A", "numeric"),
function(x, value)
{
x@.env$mutable <- value
x
})
#getter (mutable!)
setMethod("mutable", signature("A"),
function(x)
{
if (!length(x@.env$mutable)) {
message("Ah. You haven't initialized a value for 'mutable'. ",
"Lucky for you I can initialize it for you now.")
x@.env$mutable = 12
}
x@.env$mutable
})
那我可以这样做:
> a <- A()
> mutable(a)
Ah. You haven't initialized a value for 'mutable'.
Lucky for you I can initialize it for you now.
[1] 12
> mutable(a)
[1] 12
>
> inMutable(a)
Hmmm, you haven't initialized a value for 'inMutable'.
I'm afraid I can't do anything about that now.
NULL
> inMutable(a) <- 18
> inMutable(a)
[1] 18
>
显然,参考文献 类 和 R6 提供了更丰富、更强大的解决方案,但在紧要关头,这似乎是一个可行的 S4 选项。当然,我还没有在野外彻底测试它,看看它可能在哪里崩溃。