在单细胞的许多应用以及R包的开发过程中都会涉及到R中对象的概念,所以在此深入学习一下R中对象的概念,具体的是S3/S4/RC类型的学习。
面向对象基础
面向对象的3个特征:封装,继承,多态
封装:是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”;被继承的类称为“基类”、“父类”或“超类”。
多态: 指由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应。
S3类用的比较广,创建简单粗糙但是灵活,而S4类比较精细,具有跟C++一样严格的结构。
- 类 (class) 是面向对象编程的基础. 类就像是一个包装的盒子, 把对象的所有的属性都包含在其中. 可以形象地说, 类有点像一个有多种口味可供选择的冰激凌机器.
- 变量 (variable)是描述对象的具体特征的数据, 是类的属性, 可以是数字或者字符等各种类型, 在 R 语言的 S4 类中被称为存储槽 (slot). 不同的变量, 就是冰激凌机器中的不同口味的冰激凌.
- 方法 (method) 是作用于类对象的各种操作, 也是类的属性, 具体实现就是各种方程. 方法也对应于冰激凌机器上产出不同口味冰激凌的不同的按钮. 对于不同的类可以有相似的方法, 例如对不同的类都可以有 print 函数来输出类的内容. 需要注意的是, 在 R 语言的 S4 类中, 类的方法本身不属于类本身, 而是独立的方程, 这点和 Python 等语言面向对象编程有差别. 但是, 在 R 语言的 RC 类 (Reference Class) 中方法本身则属于类.
尽量不要直接引用类的slot,最好通过方法来引用。
S3对象
S3类内部是一个list,append某个list类名称,就能成为该类。
# 首先创建一个list
dnaseq = list(seq = "ATGC", length = nchar("ATGC"))
# append 一个类名"DNAseq",就这样创建了一个DNAseq类
class(dnaseq) = append(class(dnaseq),"DNAseq")
# 函数创建S3类
DNAseq <- function(seq = "ATGCATGCATGCATGCATGC"){
me <- list(
seq = seq,
length = nchar(seq)
)
# Set the name for the class
class(me) <- append(class(me), "DNAseq")
return(me)
}
seq1 <- DNAseq()
S4编程基础
S4中类的方法和类的变量是分离的,可以使用 class 函数或者 typeof 等函数来查看某个实例的类. 判断是否是 S4 类可以使用函数 isS4.
# 创建类
# S4 类的定义, 其定义中只包含类的名称和其变量, 也就是 slot, 而不包括方法.
# 指定每个slots类型时最好限定基本的类型
setClass(
Class = "IcecreamMachine",
slots = c(
strawberry = "numeric",
chocolate = "numeric",
mango = "numeric"
)
)
# 设定默认值
setClass(
Class = "IcecreamMachine",
slots = c(
strawberry = "numeric",
chocolate = "numeric",
mango = "numeric"
),
prototype = list( # 默认值设定,接受list
strawberry = 0,
chocolate = 0,
mango = 0
)
)
# 继承
# 一般只继承一个父类
# ANY 是所有的 S4 类的最基本的父类
setClass(
Class = "IcecreamStore",
slots = c(place = "character"),
contains = "IcecreamMachine"
)
# 删除类
# 删除类后, 已经存在的类的实例并不会被删除.
removeClass(Class = "IcecreamMachine")
# 查看类
# 查看类 slot 变量名, slotNames 函数.
# 获得类 slot, getSlots 函数.
# 获得类, getClass 函数.
slotNames("IcecreamMachine")
getSlots("IcecreamMachine")
getClass("IcecreamMachine")
# 类的方法
# setMethod 函数来定义
# 先设置方法原型
setMethod(
f = "show",
signature = "IcecreamMachine",
definition = function(object) {
cat("*** Wounderful Icecream Machine! ***\n")
cat("Taste:\n")
cat("\tStrawberry: ", object@strawberry, "\n")
cat("\tChocolate: ", object@chocolate, "\n")
cat("\tMango: ", object@mango, "\n")
}
)
# 设置方法原型
setGeneric(
name = "getIcecream",
def = function(object,...) {
standardGeneric("getIcecream")
})setMethod(
f = "getIcecream",
signature = "IcecreamMachine",
definition = function(object, type) {
slot(object, type) <- slot(object, type) - 1
return(1)
})
# 锁定定义, 避免误操作
lockBinding("getIcecream", .GlobalEnv)
# 查看一个 S4 类所拥有的方法
showMethods(classes="IcecreamMachine")
# 查看类是否有特定的方法
existsMethod(f = "print", signature = "IcecreamMachine")
# 直接获得某个方法的代码
getMethod(f="show", signature="IcecreamMachine")
# 属性操作
# 属性的操作可以使用@来获得,但最好还是通过对象的方法来完成
# 1. 设置一定的get函数
setGeneric(
name = "getRest",
def = function(object,...) {
standardGeneric("getRest")
}
)
setMethod(
f = "getRest",
signature = "IcecreamMachine",
definition = function(object, type) {
return(slot(object, type))
}
)