第四章 类和对象
| S3 类 | S4类 | 引用类 |
|---|---|---|
| 缺乏形式定义 | 使用setClass()定义 | 使用setRefClass()定义类 |
| 通过设置类的属性来创建对象 | 使用new()创建对象 | 使用构造函数创建对象 |
| 使用$访问属性 | 使用@访问属性 | 使用$访问属性 |
| 方法属于泛型函数 | 方法属于泛型函数 | 方法属于类 |
| 遵守修改时拷贝的语义 | 遵守修改时拷贝的语义 | 不遵守修改时拷贝的语义 |
- 遵守修改时拷贝:
- 也称为写时拷贝,或深拷贝,或值传递。
- 即声明类对象a,b 令 b <- a。则a,b对象存储数据内容相同,但指针地址不相同,修改对象属性元素互不影响。
- 不遵守修改时拷贝:
- 也称为浅拷贝,或引用传递。
- 即即声明类对象a,b 令 b <- a。则a,b对象存储相同指针地址,指向相同数据,一方修改对象属性内容时另一方也收到影响。
1. S3类
-
出自于第三代S语言,核心思想是使用list来构建对象,再给对象添加class属性或强制修改对象的class内容
-
创建S3类对象
- 方法一:
# list ---> class s <- list(name = "Han meimei", age = 20, GPA = 3.8) class(s) class(s) <- "student" class(s)- 方法二:
s1 <- list(name = "Li lei", age = 22, GPA = 3.7) attr(s1, "class") <- "student" class(s1)- 方法三:
student1 <- function(name, id, age) { s_obj <- list(s_name = name, s_id = id, s_age = age) attr(s_obj, "class") <- "student1" return(s_obj) } # 生成对象实例 s2 <- student1("Alice", 2020001, 21) s2 #访问s2对象的属性值 s2$name -
S3类的泛型函数
- 系统定义的泛型函数无需声明其泛型特性,即不需要UseMethod来指明其是泛型。
- 自定义泛型函数需要先进行UseMthod的声明。
#控制台直接输出print,可查看到print的泛型声明 #控制台直接输出plot,可查看到plot的泛型声明 #print函数的泛型,针对student1类的处理,直接打印 print.student1 <- function(student) { cat("This is a generic function for print student") } #自定义泛型函数bornyear的声明 bornyear <- function(para) { UseMethod("by") } #bornyear针对student1类的处理 bornyear.student1 <- function(student) { byear <- 2020 - student$s.age cat("Your name:", student$s.name, "Your born year:", byear) } #bornyear针对student类的处理 bornyear.student <- function(student) { cat("Your name:", student$name, "Your GPA:", student$GPA) } #bornyear针对其他不属于student1和student类的处理 bornyear.default <- function() { cat("This is a default method") }
2. S4类
- 核心思想是使用setClass函数来创建S4类的定义,通过参数指明类名称,通过参数slot传入一个列表来指明类属性以及其数据类型。最后通过new函数来生成S4类的实例。
# 定义
setClass("student_s4",
slots = list(name = "character", age = "numeric", GPA = "numeric"))
# 对象的生成
s <- new("student_s4") # 属性值为空的对象
#生成带属性值的对象
s <- new("student_s4",
name = "Xin kuzi",
age = 21L,
GPA = 3.8)
- 本质上setClass函数返回的是一个构造函数,因此可以使用一个变量名来接收setClass的返回结果,因此之后使用此变量名对象作为构造函数来生成对象。
- 方法二创建S4类和其对象。
#创建构造函数
student_s4_generator <- setClass("student_s4",
slots = list(name = "character", age = "numeric", GPA = "numeric"))
#使用构造函数生成对象
s1 <- student_s4_generator(name = "Peng Lei", age = 21, GPA = 3.9)
- 访问属性值时使用@符号
s1@name
s1@age
-
泛型函数的使用
- 对于系统泛型,可以使用setMethod直接定义,如show方法
- 对于自定义泛型,需要用setGeneric声明,且可以选择在其中将默认default的情况定义好。
#show函数是S4类的泛型函数,由系统定义,因此无需声明其泛型特点 setMethod ("show", “student",function (object) { cat (object@name, "\n") cat ("Age:", object@age, "years old\n") cat ("GPA:", object@GPA, "\n") }) #S4 自定义泛型函数 #对于S4泛型函数的一般声明 setGeneric("test", function(s4.obj) standardGeneric("test")) #对于S4泛型函数声明时处理其默认情况的状态 setGeneric("test",function(s4.obj){ print("this is generic function for default") }) #对泛型函数进行处理,指明泛型函数的名称,signature指明当前处理的类型,function用来设计 #泛型的具体方法。 setMethod("test", signature = "student_s4", function(s4.obj) { print("This is a test method for s4 class") cat("S4 object: \n", "name: ", s4.obj@name, ", age= ", s4.obj@age) }) test(s) test(s1)
3. 引用类
- 核心思想是使用setRefClass来声明引用类的构造函数,指明类名,通过field参数传入一个列表来指明类的属性,通过methods参数传入一个列表来指明类的方法。
- 因此引用类的方法是是属于引用类本身的。而S3,S4类的方法都是泛型方法,是脱离了类体定义的。
- 生成引用类对象时直接用setRefClass返回的构造函数来生成即可。
# 类的创建,属性,方法
student_rc_generator <- setRefClass(
"student_rc",
fields = list(name = "character", age = "numeric", GPA = "numeric"),
methods = list(
s_rc_func = function(obj) {
cat("Name: ", obj$name, ", Age: ", obj$age, ", GPA: ", obj$GPA)
}
)
)
#用构造函数生成对象
s_rc <- student_rc_generator(name = "Ciwei", age = 28, GPA = 3.5)
#查看对象类型以及内容
getClass(s_rc)
- 测试引用类的浅拷贝
s_rc_cp <- s_rc #定义cp变量,完全拷贝s_rc对象
s_rc_cp$name <- "Pan ni xi lin" #将cp的name改为其他值
s_rc_cp$name
s_rc$name # 原始rc对象的name受影响
s_rc_cp <- s_rc$copy() #调用copy函数,变为深拷贝,完成备份
s_rc_cp$name <- "Ciwei" #更改一方属性值
s_rc_cp$name
s_rc$name #另一方不受影响
4. 继承关系
- S3JI继承
student_s3 <- function(Pname, Page, PGPA) {
s.obj <- list(name = Pname,
GPA = PGPA,
age = Page)
attr(s.obj, "class") <- "student_s3"
return(s.obj)
}
print.student_s3 <- function(s3_obj) {
cat("This is a method for parent class")
}
s3_son <-
list(
name = "guowei VC",
age = 21,
GPA = 3.3,
country = "China"
)
class(s3_son) <- c("Inter_student_s3", "student_s3")
print(s3_son)
print.Inter_student_s3 <- function(s3_son_obj) {
cat("This is a sub-class method")
}
inherits(s3_son, "student_s3")
is(s3_son, "student_s3")
is(s3_son, "Inter_student_s3")
- S4继承
setClass("student_s4",
slots = list(name = "character", age = "numeric", GPA = "numeric"))
setClass("Inter_student_s4",
slots = list(country = "character"),
contains = "student_s4")
s4_son <-
new(
"Inter_student_s4",
name = "Jazz",
age = 21,
GPA = 3.3,
country = "China"
)
show(s4_son)
test(s4_son)
- 引用类继承
inter_student_rc_generator <-
setRefClass(
"inter_student_rc",
fields = list(country = "character"),
contains = "student_rc",
methods = list(
rc_son_func = function() {
"Just a test for sub-class method"
},
s_rc_func = function() {
"rewrite the method of parent class"
}
)
)
son_rc <-
inter_student_rc_generator(
name = "weipeng guo",
age = 30,
GPA = 0,
country = "China"
)
son_rc$s_rc_func()
son_rc$rc_son_func()
2381

被折叠的 条评论
为什么被折叠?



