简而言之(实际问题)
如何访问定义 S4 引用类的实际源代码/表达式?setRefClass
(请参阅)或返回的对象(因此在获取它之后,而不是通过调查实际源文件)?getClass("MyClass")
getRefClass("MyClass")
我做的功课
由于一切都是 R 中的对象,我可以检索源代码/表达式
1)通过简单地调查相应对象的常规功能:
foo <- function(x) print(x)
> foo
function(x) print(x)
2)通过获取特定方法的方法对象的形式方法selectMethod
:
setGeneric(name="myMethod", signature=c("x"),
def=function(x) standardGeneric("myMethod")
)
setMethod(
f="myMethod",
signature=signature(x="numeric"),
definition=function(x) print(x)
)
def <- selectMethod(f="myMethod", signature=c(x="numeric"))
# Get actual source code/expression
> attributes(def)$srcref
function(x) print(x)
但是 S4 参考类的情况似乎有所不同:
setRefClass(Class="MyClass", fields=list(x.1="character"))
def <- getRefClass("MyClass")
# Inspect object >> no expression
> def
Generator object for class "MyClass":
Class fields:
Name: x.1
Class: character
Class Methods:
"callSuper", "copy", "export", "field", "getClass", "getRefClass", "import",
"initFields", "show", "trace", "untrace"
Reference Superclasses:
"envRefClass"
def.temp <- attributes(attributes(def)$.xData$def)
# Inspect attributes >> no expression
> attributes(def.temp)
$names
[1] "fieldClasses" "fieldPrototypes" "refMethods" "refSuperClasses"
[5] "slots" "contains" "virtual" "prototype"
[9] "validity" "access" "className" "package"
[13] "subclasses" "versionKey" "sealed" "class"
# Alternatively:
> names(attributes(getClass("MyClass")))
[1] "fieldClasses" "fieldPrototypes" "refMethods" "refSuperClasses"
[5] "slots" "contains" "virtual" "prototype"
[9] "validity" "access" "className" "package"
[13] "subclasses" "versionKey" "sealed" "class"
我似乎找不到存储准确定义类的实际源代码/表达式的属性。
只是为了确保:这个表达式是我想要访问的
setRefClass(Class="MyClass", fields=list(x.1="character"))
背景/动机
我经常使用 S4 引用类 ( ),因此像类继承?setRefClass
这样的OOP 方面在我的日常工作中扮演着重要角色。我还遵循“每个文件一个定义”的范例,以使事情井井有条,因此各种类定义存储在单独的文件中,其中文件名对应于各个类的名称。
与生活中的一切一样,这种方法有一些优点,但也有一些固有的缺点:
看点一
无论长短,您最终都会得到一个与各个源文件的字母文件顺序不再匹配的继承结构。因此,简单地一个接一个地获取一个文件将导致在某个特定点出现错误,其中一些所需的超类尚未被获取。
dir.create("classes", showWarnings=FALSE)
write("setRefClass(Class=\"A\", contains=\"B\", fields=list(x.3=\"logical\"))",
file="classes/class_A.R")
write("setRefClass(Class=\"B\", contains=\"C\", fields=list(x.2=\"numeric\"))",
file="classes/class_B.R")
write("setRefClass(Class=\"C\", fields=list(x.1=\"character\"))",
file="classes/class_C.R")
class_A.R
是 folder 中的第一个文件classes
,但为了获取它,我们首先需要获取源class_B.R
(因为该文件定义了 class B
),这反过来又需要 class C
,因此需要先获取class_C.R
.
因此,正确的排序规则是:
c("class_C.R", "class_B.R", "class_A.R")
方面2
对于某些任务,您确实想要/需要“每个文件多个定义”范例:在并行化时快速轻松地将必要的对象/函数/类分配给工作进程,在实际构建包时组织代码等。
path <- "classes/classes.R"
file.create(path)
write("setRefClass(Class=\"C\", fields=list(x.1=\"character\"))",
file=path, append=TRUE)
write("setRefClass(Class=\"B\", contains=\"C\", fields=list(x.2=\"numeric\"))",
file=path, append=TRUE)
write("setRefClass(Class=\"A\", contains=\"B\", fields=list(x.3=\"logical\"))",
file=path, append=TRUE)
广告方面 1
我不喜欢保留某种手动整理参考来指定正确的采购顺序的想法,因为我认为这是计算机可以轻松为我完成的工作(找出正确的整理)。您在这里唯一需要做的就是找出每个类的超类(某种依赖关系),然后检索正确的排序规则就是小菜一碟。
编辑
如果有人感兴趣:我确实为此提出了一种工作方法。如果您想查看一些代码,请给我留言。它基于解析(不评估)相应的类 def 源文件,以调查contains
列出超类的参数的值。然后对这些超类的源文件递归地重复整个过程,直到最终得到正确的排序规则。也不是那么费时间。
这是大纲:
x <- list.files("classes", full.names=TRUE)
code <- base::parse(file=x[1])
> code
expression(setRefClass(Class="A", contains="B", fields=list(x.3="logical")))
superclasses <- eval(code[[1]][["contains"]])
> superclasses
[1] "B"
# Next: investigate source file for class 'B'
广告方面 2
我也不喜欢手动复制和粘贴,所以我实现了一个例程,允许我将存储在单个文件中或从各个对象中提取的源代码合并到单个“合并”文件(通过deparse(<source_code_expression>)
和write(..., append=TRUE)
)。至于类,正确的排序规则在这里也很重要,否则当您尝试获取合并文件时会再次出现错误。
对于这两个方面,能够选择如何获取类/函数/方法的实际源代码/表达式会很好:
- 或者基于调查存储在相应源文件中的代码 (
parse(file=*)
) - 或基于直接从相应对象访问所需信息。
第二个选项将是上述实际问题的链接。