【问题标题】:Is a S4 class an object?S4 类是对象吗?
【发布时间】:2020-04-03 11:46:19
【问题描述】:

从Chamber的Extending R中听说R中存在的一切都是对象(在Python中也是如此:Python中的类是对象,类的类是元类.)。 S4 类是对象吗? S4 类的类型是什么?

> setClass("Person",
+ slots = c(
+ name = "character",
+ age = "numeric"
+ )
+ )

> typeof(Person)
Error in typeof(Person) : object 'Person' not found
> typeof("Person")
[1] "character"

> attributes(Person)
Error: object 'Person' not found

谢谢。

【问题讨论】:

  • 您认为其中一个答案足以被接受吗?
  • 是的。提前感谢您的耐心等待。

标签: r s4


【解决方案1】:

看待这个问题的另一种方式是setClass()返回的对象是classGeneratorFunction类的对象。这绝对是一个对象。此外,由于 R 中的函数也被视为对象,因此它是一个对象。

我们将通过调整原始帖子中的代码来说明。

personGenerator <- setClass("Person",
                            slots = c(name = "character",
                                      age = "numeric"))

aPerson <- personGenerator()

此时,我们有一个生成Person 类型对象的类生成器函数和Person 的一个实例。

我们可以通过str() 函数看到这一点。

str(aPerson)
> str(aPerson)
Formal class 'Person' [package ".GlobalEnv"] with 2 slots
  ..@ name: chr(0) 
  ..@ age : num(0) 

同样,我们可以打印personGenerator() 函数的结构。

> str(personGenerator)
Formal class 'classGeneratorFunction' [package "methods"] with 3 slots
  ..@ .Data    :function (...)  
  ..@ className: chr "Person"
  .. ..- attr(*, "package")= chr ".GlobalEnv"
  ..@ package  : chr ".GlobalEnv"
> 

回到钱伯斯的名言,它在 Hadley Wickham 的 Advanced R 中被描述为:

要理解 R 中的计算,有两个口号很有帮助。

-- 存在的一切都是对象

-- 发生的一切都是函数调用

John Chambers 在Advanced R, p. 中引用。 79.

由于原问题有点含糊,如果“S4类”指的是setClass()的返回值所代表的东西,classGeneratorFunction类型的对象,这确实是一个对象。但是,如果原始问题中的“S4 类”是指setClass() 的参数中的内容,则表示Person 的事物不是对象,直到代码使用personGenerator() 函数将其实例化,如上所示。

【讨论】:

  • 那么,您对这个问题的回答是什么?我争辩说“不”,因为类本身不是对象(就像在 python 中一样),而只有生成器函数是。我认为这对于 S3 课程来说更加清晰。或者你觉得怎么样?
  • 按照我阅读问题的方式,setClass() 返回classGeneratorFunction 类型的对象。 setClass() 返回的东西是一个对象。 setClass() 的输入是参数,在 R 中也是对象。也就是说,将这些参数组合成Person 的实例是另一个函数调用的结果,为了清楚起见,我将其命名为personGenerator()。直到一行代码使用personGenerator() 创建Person 的实例,Person 本身并不存在,因此不是对象。
【解决方案2】:

据我所知,R 中的一切都是对象,但类本身并不是直接的对象。

我们可以在 R 中进行面向对象的编程。事实上,R 中的一切都是对象。 对象是具有一些属性和作用于其属性的方法的数据结构。 类是对象的蓝图。我们可以把类想象成房子的草图(原型)。它包含有关地板、门、窗等的所有细节。根据这些描述,我们建造了房子。

(来自here)。

S3

想想 S3 对象。你从来没有真正分配它们,你只是定义一个适用于这个特定类的方法:

summary.mnist <- function(x) print("HELLO!")
x <- 3
class(x) <- "mnist"
summary(x)
# [1] "HELLO!"
typeof(mnist)
# Error in typeof(mnist) : object 'mnist' not found

S4

尽管如此,在使用 S4 时,要使用一个类,您通常通过 setClass 定义一个生成器函数。这个生成器函数是闭包类型的对象,但类本身不是对象:

Person <- setClass("Person",
         slots = c(
         name = "character",
         age = "numeric"
         )
         )
typeof(Person)
# [1] "closure"
myPerson <- Person()
typeof(myPerson)
# [1] "S4"

【讨论】:

  • 谢谢。分配 setClass() 返回变量的方式和创建类对象的方式与stackoverflow.com/q/61011212 不同。我想知道哪个是首选?
  • 因为两者都是可能的,这绝对是基于意见的:我认为“我的”方法是默认的,因为这是 ?setClass() 示例中的用法。
【解决方案3】:

当通过setClass()定义一个S4类时,会创建一个classRepresentation类的对象(讽刺的是classRepresentation本身就是一个S4类)。该对象定义类,并在通过new() 创建类的实例时使用。在用户级别,这个classRepresentation 通常是不可见的,因为它存储在定义它的包的命名空间或缓存的类表(methods:::.classTable)中。不过,用户可以通过getClassDef()获取定义。

一些答案​​指出setClass() 返回classGeneratorFunction。然而,返回的 classGeneratorFunction 不是定义类的内容。事实上,在定义 S4 类时,通常的做法是忽略setClass() 的返回值,而是通过new() 编写自定义类构造函数。这是一个例子:

setClass("Person",
         slots = c(
         name = "character",
         age = "numeric"
         )
         )

# This ensures that every instance of Person is
# always initialized with a name and an age
Person <- function(name, age) {
    new("Person", name=name, age=age)
}

aPerson <- Person("John", 50)
> str(aPerson)
Formal class 'Person' [package ".GlobalEnv"] with 2 slots
  ..@ name: chr "John"
  ..@ age : num 50

# Get the class definition itself
PersonDef <- getClassDef("Person")
> str(PersonDef)
Formal class 'classRepresentation' [package "methods"] with 11 slots
  ..@ slots     :List of 2
  .. ..$ name: chr "character"
  .. .. ..- attr(*, "package")= chr "methods"
  .. ..$ age : chr "numeric"
  .. .. ..- attr(*, "package")= chr "methods"
  ..@ contains  : list()
  ..@ virtual   : logi FALSE
  ..@ prototype :Formal class 'S4' [package ""] with 0 slots
 list()
  .. .. ..$ name: chr(0) 
  .. .. ..$ age : num(0) 
  ..@ validity  : NULL
  ..@ access    : list()
  ..@ className : chr "Person"
  .. ..- attr(*, "package")= chr ".GlobalEnv"
  ..@ package   : chr ".GlobalEnv"
  ..@ subclasses: list()
  ..@ versionKey:<externalptr> 
  ..@ sealed    : logi FALSE

【讨论】:

    猜你喜欢
    • 2022-09-28
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-11
    相关资源
    最近更新 更多