跳到主要内容

R S4类

提示
  1. S4 类的概念:S4 类是 R 语言中的一种高级数据结构,提供了具有正式定义和统一创建方式的结构,增加了代码的安全性并减少错误。
  2. 创建和使用 S4 类:使用 setClass() 在 R 中定义 S4 类,通过 new() 函数创建对象,并用 @ 运算符访问类的槽(成员变量)。
  3. S4 类的高级特性:可以编写自定义方法(如使用 setMethod() 定义 show() 方法),并使用泛型函数,如 isS4() 来检查函数类型。

S4 类是对 S3 类的改进。它们具有正式定义的结构和创建对象的统一方式。

这为我们的代码增加了安全性,并防止我们意外犯下简单的错误。

在 R 中定义 S4 类

在 R 中,我们使用 setClass() 函数来定义一个类。

R 中的成员变量被称为槽(slots)。在定义类时,我们需要设置其名称和槽(以及槽的类别)。例如,

setClass("Employee_Info", slots=list(name="character", age="numeric", role="character"))

这里,我们创建了一个名为 Employee_Info 的类,它有三个槽(成员变量):nameagerole

在 R 中创建 S4 对象

在 R 中,我们使用 new() 函数来创建对象。例如,

student1 <- new("Student_Info", name = "Peter", age = 21, role = "Developer")

这里,我们通过提供类 Student_Info 的名称和在 new() 内部所有三个槽的值来创建名为 student1 的对象。

示例 1:R 中的 S4 类和对象

# 创建一个带有三个成员变量的类 "Student_Info"
setClass("Employee_Info", slots=list(name="character", age="numeric", role="character"))

# 创建该类的一个对象
employee1 <- new("Employee_Info", name = "Peter", age = 21, role = "Developer")

# 调用 employee1 对象
employee1

输出

一个 "Employee_Info" 类的对象
"name":
[1] "Peter"

"age":
[1] 21

"role":
[1] "Developer"

这里,我们使用 setClass() 函数创建了名为 Employee_Info 的 S4 类。

然后我们使用 new() 函数创建了名为 employee1 的对象

new("Employee_Info", name = "Peter", age = 21, role = "Developer")

这里,

  • name 接受 "character" 类型的值,所以我们传递了 "Peter"
  • age 接受 "numeric" 类型的值,所以我们传递了数字值 21
  • role 接受 "character" 类型的值,所以我们传递了 "Developer"

最后,我们调用了 employee1 对象。

在 R 中访问 S4 类槽

在 R 中,我们使用 @ 运算符来访问槽。例如,

# 访问 Employee_Info 类的 name 槽
employee1@name # 打印 "Peter"

这里,我们使用 @ 访问了 Employee_Info 类的 name 槽。

因此打印出了 "Peter"

示例:访问 S4 类槽

setClass("Employee_Info", slots=list(name="character", age="numeric", role="character"))

employee1 <- new("Employee_Info", name = "Peter", age = 21, role = "Developer")

# 访问 Employee_Info 的 name 槽
employee1@name # 打印 "Peter"

# 访问 Employee_Info 的 role 槽
employee1@role # 打印 "Developer"

输出

[1] "Peter"
[1] "Developer"

这里,

  • employee1@name - 访问 Employee_Info 的 name 插槽并打印 "Peter"
  • employee1@role - 访问 Employee_Info 的 role 插槽并打印 "Developer"

在 R 中修改 S4 类的插槽

我们可以使用 @ 来访问和分配新值给 R 中的一个插槽。例如,

# 访问并为 role 插槽分配新值
employee1@role <- "Designer"

# 打印新插槽值
employee1@role

这里,role 插槽的值从 "Developer" 更改为 "Designer"

示例:修改 S4 类插槽

setClass("Employee_Info", slots=list(name="character", age="numeric", role="character"))

employee1 <- new("Employee_Info", name = "Peter", age = 21, role = "Developer")

# 访问并修改 Employee_Info 的 name 插槽
employee1@name <- "Jack"

# 访问并修改 Employee_Info 的 role 插槽
employee1@role <- "Designer"

# 打印 name 插槽的修改后的值
employee1@name # 打印 "Jack"

# 打印 role 插槽的修改后的值
employee1@role # 打印 "Designer"

输出

[1] "Jack"
[1] "Designer"

这里,

  • employee1@name <- "Jack" - 将 name 插槽的值从 "Peter" 更改为 "Jack"
  • employee1@role <- "Designer" - 将 role 插槽从 "Developer" 更改为 "Designer"

R 中的 S4 泛型函数和方法

S3 类 的情况一样,S4 类的方法也属于泛型函数,而不是类本身。

使用 S4 泛型与 S3 泛型非常相似。

我们可以使用 showMethods() 函数列出所有可用的 S4 泛型函数和方法:

# 列出所有 S4 泛型方法
showMethods()

输出

Function: - (package base)
Function: != (package base)
...
...
Function: trigamma (package base)
Function: trunc (package base)

在创建了类的对象之后,当我们在交互模式下仅写下对象的名称时,它会打印对象。这是使用 S4 泛型函数 show() 完成的。

您可以在上面的列表中看到这个函数。这个函数是 S3 print() 函数的 S4 对应物。

# 不使用 show() 调用对象
employee1

# 使用 show() 调用对象
show(employee1)

这里,在两种情况下,输出将是相同的。

示例:检查函数是否为泛型函数

isS4(print)
# 输出:[1] FALSE

isS4(show)
# 输出:[1] TRUE

这里,我们使用了 isS4() 函数来检查一个函数是否为 S4 泛型函数。

因为,

  • print 不是 S4 泛型函数,所以函数返回 FALSE
  • show 是 S4 泛型函数,所以函数返回 TRUE

在 R 中编写自己的方法

在 R 中,我们可以使用 setMethod() 函数编写自己的方法。

例如,我们可以为 show() 泛型实现我们的类方法,如下所示。

setMethod("show",
"Employee_Info",

function(obj) {
cat(obj@name, "\n")
cat(obj@age, "岁\n")
cat("角色:", obj@role, "\n")
}
)

这里,我们为 show() 泛型实现了我们的类方法。

现在,如果我们像之前那样在交互模式下写出对象的名称,将执行上述代码。例如,

setClass("Employee_Info", slots=list(name="character", age="numeric", role="character"))

employee1 <- new("Employee_Info", name = "Peter", age = 21, role = "Developer")

# 创建自己的方法
setMethod("show",
"Employee_Info",

function(object) {
cat(object@name, "\n")
cat(object@age, "岁\n")
cat("角色:", object@role, "\n")
}
)

# 调用 employee1 对象
employee1

输出

Peter
21
角色:Developer

这里,我们为 show() 泛型创建了自己的方法。我们传递了 object 作为参数。

我们使用 object@ 来获取 Employee_Info 类的 nameagerole 属性的值。

现在这个方法将在我们写出对象名称时被调用。

所以输出将是:

Peter
21
角色:Developer

而不是

一个 "Employee_Info" 类的对象
"name":
[1] "Peter"

"age":
[1] 21

"role":
[1] "Developer"