这是我目前的做法:
MyClass <- R6Class(
classname = "MyClass",
public = list(
initialize = function(
) {
frames <- sys.frames()
private$.private_env <- frames[[length(frames) - 1]]$private_bind_env
},
getPrivate = function() private$.private_env
),
private = list(
.private_env = "environment",
foo = function() {
"I'm a private function"
}
)
)
test_that("private/foo", {
inst <- MyClass$new()
expect_identical(inst$getPrivate()$foo(),
"I'm a private function"
)
})
想知道是否有更好/更“内置”的方式来对私有函数进行单元测试。
你可以像这样简化它:
MyClass <- R6Class(
classname = "MyClass",
public = list(
getPrivate = function() private
),
private = list(
foo = function() "I'm a private function"
)
)
library(testthat)
test_that("private/foo", {
inst <- MyClass$new()
expect_identical(inst$getPrivate()$foo(),
"I'm a private function"
)
})
但是当然这仍然涉及将getPrivate
函数添加到您的类中,您通常可能不想这样做。
您实际上可以通过检查公共功能来访问私有环境。 在这个例子中,它使用environment(inst$pub)$private
来获取私有环境:
MyClass <- R6Class(
classname = "MyClass",
public = list(
pub = function() "I'm a public function"
),
private = list(
foo = function() "I'm a private function"
)
)
test_that("private/foo", {
inst <- MyClass$new()
# Get the private environment
priv <- environment(inst$pub)$private
expect_identical(priv$foo(),
"I'm a private function"
)
})
这在https://github.com/wch/R6/blob/master/doc_extra/R6.pdf的第二页上有图解
啊,很不错! 谢谢指点!
相关: https :
对于我的 SO 问题,这次谈话似乎主要是“不”。 我想知道默认情况下分配“@”符号(或类似的东西)以提供对 R6 类中所有私有组件的内置访问会有多困难。 inst@foo
会很方便。
@zkurtz截至目前,您可以通过obj$.__enclos_env__$private
访问私有环境。 例如:
A <- R6Class("A",
private = list(x=1)
)
a <- A$new()
a$.__enclos_env__$private$x
#> [1] 1
最初提出这个问题时,我认为这是不可能的。
我通常只是为此编写一个小的内部get_private()
函数,并在测试中使用它。
这些都是不错的选择,但都有些笨拙。 我想我不清楚a$x
(或a@x
,如果$
必须为公众保留)是否有充分的理由不能或不应该被定义作为a$.__enclos_env__$private$x
一个非常简短的版本,根据@wch的例子。
或者,回到我原来的 SO 帖子的意图,有一个“锁定”公共组件的选项可能很方便,这样如果b
被锁定, x$b = 5
将返回错误。
@zkurtz提出的快捷方式和锁定功能确实很好!
它以这种方式工作的原因是因为这就是$
运算符在 R 中索引环境时的工作方式。 您可以定义自己的$
S3 方法来执行您想要的任何操作,但它会带来显着的性能损失,这对于您的用例来说可能是也可能是不可接受的。 R6 背后的主要动机之一是轻量级。
我不知道是否可以用@
做同样的事情。
如果您想要一个“锁定”变量(只能从类内部更改),则必须使用活动绑定,如下所示:
A <- R6Class("A",
active = list(
x = function(value) {
if (missing(value)) private$.x
else stop("Can't assign value to x")
}
),
private = list(.x = 1)
)
a <- A$new()
a$x
#> [1] 1
a$x <- 2
#> Error in (function (value) : Can't assign value to x
使用像这样的主动绑定会产生性能成本。
如果你想要一个真正锁定的值(不能从类内部或外部改变),你可以在初始化函数中使用lockBinding()
:
A <- R6Class("A",
public = list(
initialize = function() {
lockBinding("x", self)
},
x = 1
)
)
a <- A$new()
a$x
#> [1] 1
a$x <- 2
#> Error in a$x <- 2 : cannot change value of locked binding for 'x'
@wch 太棒了,我会试试的。 如果我理解正确, lockBinding
正是我一直在寻找的(假设它没有大的性能问题)。
@zkurtz很棒。 我相信lockBinding
没有任何性能成本。
最有用的评论
@zkurtz截至目前,您可以通过
obj$.__enclos_env__$private
访问私有环境。 例如:最初提出这个问题时,我认为这是不可能的。