【问题标题】:How to tell an R reference class object to update its method definitions?如何告诉 R 引用类对象更新其方法定义?
【发布时间】:2014-03-18 22:17:31
【问题描述】:

我正在寻找一种方法来告诉引用类的实例忘记它的一个方法定义。例如,我创建了MyReferenceClass 类和一个名为my_object 的实例,我可以调用print_hello 方法,一切正常:

MyReferenceClass <- setRefClass("MyReferenceClass",
    methods = list(
        print_hello = function(){
            print("hello")
        }
    )
)
my_object <- MyReferenceClass$new()
my_object$print_hello() # "hello"

如果我通过添加新方法 (print_goodbye) 来更新类定义,我现有的对象将能够使用它。但是如果我更改了之前定义的方法(print_hello),它就不会更新:

MyReferenceClass <- setRefClass("MyReferenceClass",
    methods = list(
        print_hello = function(){
            print("hello_again")
        },
        print_goodbye = function(){
            print("goodbye")
        }
    )
)
my_object$print_goodbye() # "goodbye" => it works
my_object$print_hello() # "hello" => it doesn't work

有没有办法告诉my_object 忘记它对print_hello 的定义?这不起作用:my_object$print_hello &lt;&lt;- NULL

【问题讨论】:

  • 仅供参考:您提供的类 def 缺少 fields(name1="character") 并且实际上不清楚 path 参数的作用是什么。我很惊讶它在调用 print_hello() 和/或 print_goodbye() 方法时没有导致错误,因为您没有指定默认值。
  • 抱歉,我从一个更完整的示例中制作了示例代码,但忘记删除了。我会更新问题。

标签: r reference-class


【解决方案1】:

AFAIK 答案是否定的,当试图“事后”通知对象类定义更改时,即在实例化/创建之后。

一旦您创建了 S4 类的实例,该对象就会像您创建对象时一样“绑定”到类 def。在我看来,这是完全有道理的。不确定以前缺少的方法(即print_goodbye())的“成功”更新是“偶然”起作用还是实际上是所需的行为。

处理更新的类定义的推荐方法

我的建议:如果您决定想要/需要更新您的类定义,那么重新采购整个项目代码会更安全。这样,您就可以确保创建实际实例之前一切就绪。我认为其他任何东西都是非常不稳定的基础。

如果你还是决定破解

可能有一些肮脏的方法来破解实际包含类 def 的引用类实例的隐藏 .refClassDef 对象字段(请参阅my_object$.refClassDef)。但是设置这个字段(即在上面使用&lt;-)不起作用:

my_object$.refClassDef <- MyReferenceClass
Error in envRefSetField(x, what, refObjectClass(x), selfEnv, value) : 
  '.refClassDef' is not a field in class "MyReferenceClass"

也没有通过assign()进行明确分配:

assign(".refClassDef", MyReferenceClass, my_object)
Error in assign(".refClassDef", MyReferenceClass, my_object) : 
  cannot change value of locked binding for '.refClassDef'

更深入的破解可能涉及查看attributes(my_object$.refClassDef)。 在那里,您可能会找到构成 ref 类 def 的实际部分。但是,我不知道是否即使更改任何内容都会“立即”反映出来。

另外,resetClass() 可能会为您提供更多见解。


更新:2014-03-19

为了处理您的缓存方法,我想到了两种方法:

1。最明显的方式:使用copy()

?setRefClass

MyReferenceClass <- setRefClass("MyReferenceClass",
    methods = list(
        print_hello = function(){
            print("hello")
        }
    )
)
my_object <- MyReferenceClass$new()
MyReferenceClass <- setRefClass("MyReferenceClass",
    methods = list(
        print_hello = function(){
            print("hello_again")
        },
        print_goodbye = function(){
            print("goodbye")
        }
    )
)

复制前:

my_object$print_hello()
[1] "hello"

复制后:

my_object <- my_object$copy()
my_object$print_hello()
[1] "hello_again"

2。在attributes(my_object$.refClassDef)$refMethods 进行黑客攻击(大纲,尚未运行)

尽管我不建议真正依赖这样的东西,但黑客始终是深入了解事物运作方式的好方法。

在这种情况下,我们可以尝试修改attributes(my_object$.refClassDef)$refMethods,这是一个包含实际方法定义的环境,因为我猜这是调用方法时对象“看起来”的地方。

覆盖实际的方法defs没有问题,但它似乎没有立即生效。我猜有更多“链接”到涉及的“旧”类 def 需要以类似的方式手动更新。

请注意,my_object 仍然具有打印 "hello" 的方法 print_hello

attributes(my_object$.refClassDef)$refMethods$print_hello
Class method definition for method print_hello()
function () 
{
    print("hello")
}

这是覆盖函数的样子:

ensureRecentMethods <- function(obj, classname) {
    ## Get generator //
    gen <- getRefClass(classname)

    ## Get names of methods belonging to the class of 'obj' //
    ## This will serve as an index for the update
    idx1 <- names(Filter(function(x) {attr(x, "refClassName") == class(obj)}, 
        as.list(attributes(obj$.refClassDef)$refMethods))
    )
    #idx2 <- names(Filter(function(x) {attr(x, "refClassName")==gen$className}, 
    #    as.list(gen$def@refMethods)
    #))
    ## Note:
    ## 'idx2' could be used to enforce some validity checks such as 
    ## "all old methods must also be present in the updated class def"

    ## Overwrite //
    for (ii in idx1) {
        ## Note how we are overwriting the old method defs in environment
        ## 'attributes(obj$.refClassDef)$refMethods' with the updated 
        ## definitions taken from the generator of the updated class 
        ## 'gen$def@refMethods[[ii]]' by making use of the index retrieved 
        ## one step before ('idx1')
        expr <- substitute(
            assign(x=X, value=VALUE, envir=ENVIR),
            list(
                X=ii,
                VALUE=gen$def@refMethods[[ii]],
                ENVIR=attributes(obj$.refClassDef)$refMethods
            )
        )
        eval(expr)
    }

    ## As at the end of the day ref class objects are nothing more than 
    ## environments, there is no need to explicitly return the actual 
    ## ref class object 'obj' as the original object has already 
    ## been updated (pass-by-reference vs. pass-by-value)
    return(TRUE)
}

应用它:

ensureRecentMethods(obj=my_object, classname="MyReferenceClass")

即使print_hello 的定义确实被覆盖了,该对象仍然以某种方式获取“旧”版本:

attributes(my_object$.refClassDef)$refMethods$print_hello 
## Note the updated method def!

Class method definition for method print_hello()
function () 
{
    print("hello_again")
}

my_object$print_hello()
[1] "hello"

【讨论】:

  • 只是为了上下文,我想更新类定义而不重新创建实例的原因是创建实例需要很长时间,因为我必须处理大文件。我目前的解决方法是将这些文件缓存为 rds 对象,但我正在寻找替代解决方案。
  • 明白了。曾经遇到过类似的问题。但在这种情况下,解决方案可能会像您想象的那样简单:使用每个引用类附带的$copy() 方法。我会更新答案
【解决方案2】:

利用 my_class@generator$def@refMethods

在原始类中包含一个update 方法怎么样,就像这里所做的那样,

Manual modifications of the class definition of a Reference Class instance

【讨论】:

    猜你喜欢
    • 2019-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多