R--面向对象编程

具体再学习R中S3、S4、RC系统

Posted by CHY on November 30, 2020

在单细胞的许多应用以及R包的开发过程中都会涉及到R中对象的概念,所以在此深入学习一下R中对象的概念,具体的是S3/S4/RC类型的学习。

面向对象基础

面向对象的3个特征:封装,继承,多态
封装:是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”;被继承的类称为“基类”、“父类”或“超类”。
多态: 指由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应。
S3类用的比较广,创建简单粗糙但是灵活,而S4类比较精细,具有跟C++一样严格的结构。

  1. 类 (class) 是面向对象编程的基础. 类就像是一个包装的盒子, 把对象的所有的属性都包含在其中. 可以形象地说, 类有点像一个有多种口味可供选择的冰激凌机器.
  2. 变量 (variable)是描述对象的具体特征的数据, 是类的属性, 可以是数字或者字符等各种类型, 在 R 语言的 S4 类中被称为存储槽 (slot). 不同的变量, 就是冰激凌机器中的不同口味的冰激凌.
  3. 方法 (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))
    }
)

参考链接

S4 编程基础
R语言基础教程——第7章:面向对象编程(S3类)
R语言基础教程——第7章:面向对象编程(S4类)