読者です 読者をやめる 読者になる 読者になる

Reference class の initialize メソッドについて

R

initialize メソッドの挙動 を調べていたら、R devel の ML (Ref Classes: bug with using '.self' within initialize methods?)TractoR というパッケージのコードを見つけ、これを参考に uninitializedField に初期値を与えてみるコードを書きました。

myclass0 <- setRefClass(
  Class = "myclass0",
  fields = c("field1"),
  methods = list(
    
    initialize = function(...) {
        object <- initFields(...)
        print(initFields)
        if (class(object$field1) == "uninitializedField") object$field1 <- 5
        return(object)
    },

    myfunc0 = function(a) {
      a - field1
    }

  )
)

myclass1 <- setRefClass(
  Class = "myclass1",
  fields = c("field1"),
  methods = list(
    
    initialize = function(...) {
        object <- callSuper(...)
        print(callSuper)
        if (class(object$field1) == "uninitializedField") object$field1 <- 5
        return(object)
    },

    myfunc0 = function(a) {
      a - field1
    }

  )
)


test0 <- myclass0$new()
test0$myfunc0(5)
test1 <- myclass1$new()
test1$myfunc0(5)
test0 <- myclass0$new(field1 = 10)
test0$myfunc0(5)

ここで、myclass0 の initialize では initFields(...) を呼び出していて、myclass1 の initialize では callSuper(...) を呼んでいるという違いがあります。


これらを実行してみると、

> test0 <- myclass0$new()
function (...) 
{
    if (length(list(...)) > 0) 
        initFieldArgs(.self, .refClassDef, as.environment(.self), 
            ...)
    else .self
}
<environment: 0x0566f5c4>
attr(,"mayCall")
character(0)
attr(,"name")
[1] "initFields"
attr(,"refClassName")
[1] "envRefClass"
attr(,"superClassMethod")
[1] ""
attr(,"class")
[1] "refMethodDef"
attr(,"class")attr(,"package")
[1] "methods"
> test1 <- myclass1$new()
function (...) 
{
    if (length(list(...)) > 0) 
        initFieldArgs(.self, .refClassDef, as.environment(.self), 
            ...)
    else .self
}
<environment: 0x05760468>
attr(,"mayCall")
character(0)
attr(,"name")
[1] "initFields"
attr(,"refClassName")
[1] "envRefClass"
attr(,"superClassMethod")
[1] ""
attr(,"class")
[1] "refMethodDef"
attr(,"class")attr(,"package")

となって、あぁやっぱり initialize 中の callSuper(...) は initFields(...) を呼んでいるんだなぁという事が分かりました確認できました。
methods pacakge のソースを読めばいいのですが、読んでもよく分からなかったので・・・

# refClass.R - Lines 162-177
    ## assign references to the object and to its class definition
    assign(".self", .Object, envir = selfEnv)
    assign(".refClassDef", classDef, envir = selfEnv)
    if(is.function(classDef@refMethods$finalize))
        reg.finalizer(selfEnv, function(x) x$.self$finalize())
    if(is.function(classDef@refMethods$initialize))
        .Object$initialize(...)
    else {
        if(nargs() > 1) {
            .Object <-
                methods::initFieldArgs(.Object, classDef, selfEnv, ...)
            ## reassign in case something changed
            assign(".self", .Object, envir = selfEnv)
        }
        .Object
    }


実行結果はこんな感じでした。

> test0 <- myclass0$new()
> test0$myfunc0(5)
[1] 0
> test1 <- myclass1$new()
> test1$myfunc0(5)
[1] 0
> test0 <- myclass0$new(field1 = 10)
> test0$myfunc0(5)
[1] -5