R语言面向对象_S4

2021-11-02 15:01:10 浏览数 (1)

R语言面向对象_S4

1. 概述

最近回头看了看以前写的一些数据处理,函数用的很多,总感觉有点力不从心,想想可能是没有面向对象的原因。虽然R6面向对象比较新,但是像Bioconductor社区的包都是基于S4,所以还是得学习这部分的内容。今天服务器搬家,闲着更新下公众号,不然都要废了

2. S4 对象

S4对象源自S3的发展,但是相比较S4对象定义更加严格,严格的定义,参数声明,继承等内容

2.1 S4对象的创建

代码语言:javascript复制
setClass(class,representation,prototype,contains=character(),
  validity,access,where,version,sealed,package,
  S3methods=FALSE,slots)
  • class为类名
  • slots:定义属性和属性类型,此部分类似python的init
  • contains=character():定义父类,继承关系

2.1.1 实例化

代码语言:javascript复制
# 定义对象
# 使用list将参数传递给slots
setClass("Person",slots=list(name="character",age="numeric"))

# 实例化对象

father <- new("Person",name="F",age=44)


father

# 显示创建了一个person的实例化对象father,其中
# father有两个属性,姓名和年龄
## An object of class "Person"
## Slot "name":
## [1] "F"
## 
## Slot "age":
## [1] 44
代码语言:javascript复制
class(father)
## [1] "Person"
## attr(,"package")
## [1] ".GlobalEnv"
# 查看father对象为S4的对象
otype(father)# 此函数需要pryr包
## [1] "S4"

2.1.3 继承

代码语言:javascript复制
# 创建一个S4对象Person
setClass("Person",slots=list(name="character",age="numeric"))

# 创建Person的子类定义属性father和mother
# contains表示并继承person
setClass("Son",slots=list(father="Person",mother="Person"),contains = "Person")

# 实例化Person对象,建立father和mother

father <- new("Person",name="F",age=44)
mother <- new("Person",name="M",age=39)

# 实例化一个Son对象命名为son
# 年龄为16,属性为father和mother
# son的类中其实并没有定义age和name,但是可以使用person的属性,也就是继承
son <- new("Son",name="S",age=16,father=father,mother=mother)

# 查看son对象的name属性
son@name
## [1] "S"
# 查看son对象的age属性
son@age
## [1] 16
# 查看son对象的father属性
son@father
## An object of class "Person"
## Slot "name":
## [1] "F"
## 
## Slot "age":
## [1] 44
# 查看son对象的mother属性
son@mother
## An object of class "Person"
## Slot "name":
## [1] "M"
## 
## Slot "age":
## [1] 39
# 查看son类型
otype(son)
## [1] "S4"
# 查看son@name的属性
otype(son@name)
## [1] "base"
#  查看son@mother的属性
otype(son@mother)
## [1] "S4"
# 用isS4()检查S4对象的类型
isS4(son)
## [1] TRUE
isS4(son@name)
## [1] FALSE
isS4(son@mother)
## [1] TRUE

2.1.4 prototype定义属性默认值

代码语言:javascript复制
setClass("Person",slots=list(name="character",age="numeric"))

# 属性age为空
a <- new("Person",name="a")
a
## An object of class "Person"
## Slot "name":
## [1] "a"
## 
## Slot "age":
## numeric(0)
# 设置属性age的默认值为20

setClass("Person",slots=list(name="character",age="numeric"),prototype=list(age=20))

# 初始化b对象
b <- new("Person",name="b")

# 属性age的默认值是20
b 
## An object of class "Person"
## Slot "name":
## [1] "b"
## 
## Slot "age":
## [1] 20

2.1.5 类型检查

代码语言:javascript复制
setClass("Person",slots=list(name="character",age="numeric"))
#传入错误age类型
# numeric 为数字类型
bad <- new("Person",name="bad",age="abc")

###
#Error in validObject(.Object) : 类别为“Person”的对象不对: invalid object for slot "age" in class "Person": got class "character", should be or extend class "numeric"       
# 设置age的非负检查
setValidity("Person",function(object){
  if(object@age <= 0) stop("Age is negative.")
})

# 传入小于0的年龄
bad2 <- new("Person",name="bad",age=-1)

###
Error in validityMethod(object) : Age is negative.

2.1.6 从一个已经实例化的对象中创建新对象

S4对象,还支持从一个已经实例化的对象中创建新对象,创建时可以覆盖旧对象的值

代码语言:javascript复制
setClass("Person",slots=list(name="character",age="numeric"))

# 创建一个对象实例n1

n1 <- new("Person",name="n1",age=19)
n1
## An object of class "Person"
## Slot "name":
## [1] "n1"
## 
## Slot "age":
## [1] 19

# 从实例n1中,创建实例n2,并修改name的属性值
n2 <- initialize(n1,name="n2")
n2
## An object of class "Person"
## Slot "name":
## [1] "n2"
## 
## Slot "age":
## [1] 19

2.2 使用@访问对象属性

代码语言:javascript复制
setClass("Person",slots=list(name="character",age="numeric"))
a <- new("Person",name="a")

# 访问S4对象的属性

a@name
## [1] "a"
slot(a,"name")
## [1] "a"
# 错误的访问
#a$name
#a[1]

2.3 S4的泛型函数

  • 普通函数的定义和调用
代码语言:javascript复制
work <- function(x) cat(x,"is working")
work("Conan")
## Conan is working
  • S4的方式将定义与实现分离
代码语言:javascript复制
# 定义数据对象类型
setClass("Person",slots=list(name="character",age="numeric"))
# 定义接口函数
setGeneric("work",function(object) standardGeneric("work"))
## [1] "work"
# 定义实现函数
setMethod("work",signature(object="Person"),function(object) cat(object@name,"is working"))
## [1] "work"
# 实例化对象person
a <- new("Person",name="Conan",age=16)

# 将数据对象通过接口函数传入
work(a)
## Conan is working

2.4 查看S4对象的函数

当我们使用S4对象进行面向对象封装后,我们还需要能查看到S4对象的定义和函数定义,

代码语言:javascript复制
library(pryr)
# 检查work的类型
ftype(work)
# [1] "s4"      "generic"
# 直接查看work函数
work
#standardGeneric for "work" defined from package ".GlobalEnv"
#
#function (object) 
#standardGeneric("work")
#<environment: 0x000001d98cf9fad8>
#Methods may be defined for arguments: object
#Use  showMethods("work")  for currently available ones.
# 查看work函数的显示定义
showMethods(work)
#Function: work (package .GlobalEnv)
#object="Person"
# 查看Person对象的work函数现实
getMethod("work","Person")
#Method Definition:
#
#function (object) 
#cat(object@name, "is working")
#
#Signatures:
#        object  
#target  "Person"
#defined "Person"

# 检查Person对象有没有work函数
existMethod("work","Person")
hasMethod("work","Person")

2.6 示例

研发一个数据处理程序

代码语言:javascript复制
setClass("check",slots=list(name="character"))

# 定义check_na类,并继承check,

setClass("check_na",contains = "check",slots=list(x="numeric"))

car_na <- new("check_na",name="car_na",x=mtcars$mpg)
iris_na <- new ("check_na",name="iris_na",x=iris$Sepal.Width)

#定义接口
setGeneric("do",function(obj,...){
  standardGeneric("do")
})
setMethod("do","check_na",function(obj,...){
  print("看看有多少缺失")
  sum(is.na(obj@x))
})

do(car_na)
do(iris_na)
#[1] "看看有多少缺失"
#[1] 0

3. 结束语

这不是我想要的东西,S4对象不再研究

考虑重新用R6

love & peace

0 人点赞