我在实现访问附加到引用类对象的属性的一致行为时遇到了一些麻烦。例如,
testClass <- setRefClass('testClass',
methods = list(print_attribute = function(name) print(attr(.self, name))))
testInstance <- testClass$new()
attr(testInstance, 'testAttribute') <- 1
testInstance$print_attribute('testAttribute')
R 控制台愉快地打印NULL
。但是,如果我们尝试另一种方法,
testClass <- setRefClass('testClass',
methods = list(initialize = function() attr(.self, 'testAttribute') <<- 1,
print_attribute = function(name) print(attr(.self, name))))
testInstance <- testClass$new()
testInstance$print_attribute('testAttribute')
现在我们1
有如预期的那样。请注意,<<-
运算符是必需的,可能是因为分配给与.self
分配给引用类字段具有相同的限制。请注意,如果我们试图在构造函数之外进行分配,比如说
testClass <- setRefClass('testClass',
methods = list(set_attribute = function(name, value) attr(.self, name) <<- value,
print_attribute = function(name) print(attr(.self, name))))
testInstance <- testClass$new()
testInstance$set_attribute('testAttribute', 1)
我们会被打耳光
Error in attr(.self, name) <<- value :
cannot change value of locked binding for '.self'
事实上,文档?setRefClass
解释说
整个对象可以通过保留名称在方法中引用
.self
......这些字段是只读的(修改这些引用没有意义),但有一个例外。原则上,.self
可以在$initialize
方法中修改该字段,因为在此阶段仍在创建对象。
我对这一切都很满意,并同意作者的决定。但是,我担心的是以下内容。回到上面的第一个例子,如果我们尝试请求attr(testInstance, 'testAttribute')
,我们从全局环境中看到它是1
!
据推测,.self
在引用类对象的方法中使用的那个存储在相同的内存位置testInstance
——它是同一个对象。因此,通过在全局环境中成功设置属性testInstance
,而不是作为.self
参考(如第一个示例所示),我们是否无意中触发了全局环境中整个对象的副本?还是以某种方式存储属性的方式“有趣”,即对象可以驻留在同一内存中,但其属性因调用环境而异?
attr(.self, 'testAttribute')
我看不出为什么is NULL
but attr(testInstance, 'testAttribute')
is的其他解释1
。绑定 .self
被一劳永逸地锁定,但这并不意味着它引用的对象不能更改。如果这是所需的行为,那似乎是一个陷阱。
最后一个问题是,是否attr<-
应该在引用类对象上避免上述结果,至少在对象方法中使用结果属性的情况下是这样。