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的泛型函数
- 普通函数的定义和调用
work <- function(x) cat(x,"is working")
work("Conan")
## Conan is working
- S4的方式将定义与实现分离
# 定义数据对象类型
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