加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【R】R语言基于S4的面向对象编程(Part1)

(2016-05-31 16:23:06)
标签:

r

面向对象

s4

分类: R学习笔记

前言

S4对象系统具有明显的结构化特征,更适合面向对象的程序设计。Bioconductor社区,以S4对象系统做为基础架构,只接受符合S4定义的R包。

要点提示

setClass:定义一个类,一个类可以有多个函数

setGeneric:定义一个函数的接口(类似于变量使用前的申明,或C语言中的函数原型),同一个函数接口可以有多个具体实现,且这些具体实现可以属于不同的类

setMethod:在setGeneric中定义的函数的具体实现,属于某一个特定的类


目录

1.   S4对象介绍

2.   创建S4对象

3.   访问对象的属性

4.   S4的泛型函数

5.   查看S4对象的函数

6.   S4对象的使用


1.   S4对象介绍

S4对象系统是一种标准的R语言面向对象实现方式,S4对象有明确的类定义,参数定义,参数检查,继承关系,实例化等的面向对象系统的特征。


2.   创建S4对象

本文的系统环境

²Linux: Ubuntu Server 12.04.2 LTS 64bit

²R: 3.0.1 x86_64-pc-linux-gnu

转载这篇文章时,我用的系统环境

²Windows 7

²R: 3.3.0

 

为了方便我们检查对象的类型,引入pryr包作为辅助工具。关于pryr包的介绍,请参考文章:[撬动R内核的高级工具包pryr](http://blog.fens.me/r-pryr/)

# 加载pryr

> library(pryr)

 

2.1 如何创建S4对象?

由于S4对象是标准的面向对象实现方式, 有专门的类定义函数 setClass() 和类的实例化函数new() ,我们看一下setClass()new()是如何动作的。


2.1.1 setClass()

查看setClass的函数定义

setClass(Class, representation, prototype, contains=character(),validity, access, where, version, sealed, package,S3methods = FALSE, slots)

参数列表:

·        Class: 定义类名

·        slots: 定义属性和属性类型

·        prototype: 定义属性的默认值

·        contains=character(): 定义父类,继承关系

·        validity: 定义属性的类型检查

·        where: 定义存储空间

·        sealed: 如果设置TRUE,则同名类不能被再次定义

·        package: 定义所属的包

·        S3methods: R3.0.0以后不建议使用

·        representation R3.0.0以后不建议使用

·        access R3.0.0以后不建议使用

·        version R3.0.0以后不建议使用


2.2 创建一个S4对象实例

# 定义一个S4对象

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

 

# 实例化一个Person对象

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

 

# 查看father对象,有两个属性nameage

> father

An object of class "Person"

Slot "name":

[1] "F"

 

Slot "age":

[1] 44

 

# 查看father对象类型,为Person

> class(father)

[1] "Person"

attr(,"package")

[1] ".GlobalEnv"

 

# 查看father对象为S4的对象

> otype(father)

[1] "S4"


2.3 创建一个有继承关系的S4对象

# 创建一个S4对象Person

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

 

# 创建Person的子类

> setClass("Son",slots=list(father="Person",mother="Person"),

           contains="Person")

 

# 实例化Person对象

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

> mother <- new("Person",name="M",age=39)

 

# 实例化一个Son对象

> 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属性

> slot(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] "primitive"

 

# 检查son@mother属性类型

> otype(son@mother)

[1] "S4"

 

# isS4(),检查S4对象的类型

> isS4(son)

[1] TRUE

> isS4(son@name)

[1] FALSE

> isS4(son@mother)

[1] TRUE


2.4 S4对象的默认值

> 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))

 

# 属性age为空

> b<-new("Person",name="b")

 

# 属性age的默认值是20

> b

An object of class "Person"

Slot "name":

[1] "b"

 

Slot "age":

[1] 20


2.5 S4对象的类型检查

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

 

# 传入错误的age类型

> bad<-new("Person",name="bad",age="abc")

Error in validObject(.Object) :

  invalid class “Person” object: 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.")

+ })

Class "Person" [in ".GlobalEnv"]

 

Slots:

Name:       name       age

Class: character   numeric

 

# 修传入小于0的年龄

> bad2<-new("Person",name="bad",age=-1)

Error in validityMethod(object) : Age is negative.


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

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

> 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


3. 访问对象的属性

S3对象中,一般我使用$来访问一个对象的属性,但在S4对象中,我们只能使用@来访问一个对象的属性

> 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

Error in a$name : $ operator not defined for this S4 class

> a[1]

Error in a[1] : object of type 'S4' is not subsettable

> a[[1]]

Error in a[[1]] : this S4 class is not subsettable


4. S4的泛型函数

S4的泛型函数实现有别于S3的实现,S4分离了方法的定义和实现,如在其他语言中我们常说的接口和实现分离。通过setGeneric()来定义接口,通过setMethod()来定义现实类。这样可以让S4对象系统,更符合面向对象的特征。

普通函数的定义和调用

> work<-function(x) cat(x, "is working")

> work('Conan')

Conan is working

让我来看看如何用R分离接口和现实

# 定义Person对象

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

 

# 定义泛型函数work,即接口

# 创建了一个泛型函数work,但并没有与Person关联起来

> setGeneric("work",function(object) standardGeneric("work"))

[1] "work"

 

# 定义work的现实,并指定参数类型为Person对象

# 在这里与Person关联起来了

> setMethod("work", signature(object = "Person"), function(object) cat(object@name , "is working") )

[1] "work"

 

# 创建一个Person对象a

> a<-new("Person",name="Conan",age=16)

 

# 把对象a传入work函数

> work(a)

Conan is working

通过S4对象系统,把原来的函数定义和调用这2步,变成了4

·        定义数据对象类型

·        定义接口函数

·        定义实现函数

·        把数据对象以参数传入到接口函数,执行实现函数

通过S4对象系统,组成的是一个结构化的,完整的面向对象实现。


5. 查看S4对象的函数

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

还以上节中Personwork的例子

 

# 检查work的类型

> ftype(work)

[1] "s4"      "generic"

 

# 直接查看work函数

> work

standardGeneric for "work" defined from package ".GlobalEnv"

function (object)

standardGeneric("work")

 

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"

 

> selectMethod("work", "Person")

Method Definition:

function (object)

cat(object@name, "is working")

Signatures:

        object

target  "Person"

defined "Person"

 

# 检查Person对象有没有work函数

existsMethod("work", "Person")

[1] TRUE

> hasMethod("work", "Person")

[1] TRUE





from: http://blog.fens.me/r-class-s4/
有少量改动

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有