R语言S4对象介绍

编程对象简介

R虽然有S3、S4、(RC)Reference classes 三个面向对象的系统和一个多由C语言构建的基本类型(base types),但是一般S3就能够满足大多数的R编程。因为,在R中我们通常都是为已存在的通用函数(如print()、summary()和plot())创建相当简单的对象和方法,然后在进行调用。由于S3是一个非常随意的系统,没有正式的类定义;加上S4系统与S3相似而且更加正式,因此先了解S4后,再回过头看S3是一个非常取巧的学习方法。

S4对象介绍

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

所有的S4相关代码均存在methods包中,S4非常复杂,但是好在有很多权威的资料供我们参考。如:

Bioconductor 中开发课程资料

Software for data analysis: Programming with R - by John Chambers

[Martin Morgan’s answers to S4 questions on stackoverflow](http://stackoverflow.com/search?tab=votes&q=user%3a547331%20%5bs4%5d%20is%
3aanswe)

S4 框架介绍

S4的泛型函数分离了方法的定义和实现,也就是常说的接口和调用分离。即通过S4对象系统,把原来的函数定义和调用2步法,分成了4步进行:

  1. 定义数据对象类型

  2. 定义接口类(泛型函数)

  3. 定义方法类

  4. 把数据对象以参数传入到泛型函数,调用实现函数

CLASS 泛型类 方法类
setClass():定义 setGeneric() setMethod()
class:类名 创建新泛型类或将现有函数转换为泛型类 通过联系泛型类、class中的参数,并利用function实现函数调用
slots: 属性和类型 name :泛型类名 f :泛型类名
prototype: 属性的默认值 def:函数定义 definition:方法定义
contains: 定义父类,继承源头 signature : the method dispatch signature signature:class类名
validity: 属性的类型检查
sealed: 同名类是否能被再定义
new():通过类名和slots中的参数创建对象

定义类并创建对象

如上表所示,S4的类(class)可以通过setClass()实现,而new()则可以通过类名和slots中的参数创建跟该类相关的对象。

# 创建Class - Person
setClass("Person", slots = list(name = "character", age = "numeric"))
​
# 创建Class - Employee,利用contains表明参数继承自Person。
setClass("Employee", slots = list(boss = "Person"), contains = "Person")
​
# 设置基于Class - Person的对象
alice <- new("Person", name = "Alice", age = 40)
​
# 设置基于Class - Employee的对象
john <- new("Employee", name = "John", age = 20, boss = alice)
str(alice)
str(john) # 可以看到jhon多了一个slot,即@boss

通过prototype 设置初始值,当创建对象时,如果slots中的属性为空时,该对象会调用初始值作填入。

# 创建Class - Person
setClass("Person", slots = list(name = "character", age = "numeric"), prototype = list(age = 20))
​
alice <- new("Person", name = "Alice")
str(alice)

通过setValidity设置类属性检查,当通过new()创建对象时,setValidity将对slots 中的参数进行检查、输出报告。

setClass("Person", slots = list(name = "character", age = "numeric"), prototype = list(age = 20))
​
setValidity("Person",  
 function(object){
 if (object@age < 18)
 "he/she is too yong to get the job."
 })
​
new("Person", name = "Penny", age = 17)

如果定义的S4类(class)中包含(继承)了S3类或基础类,该S4类(class)中将有一个包含底层基础类或S3对象的.Data slots:

setClass("RangedNumeric",
 slots = list(min = "numeric", max = "numeric"),
 contains = "numeric")
rn <- new("RangedNumeric", 1:10, min = 1, max = 10)
str(rn)

构造泛型类和方法类

setGeneric()可以创建或将原有的类转换为泛型类,如果setGeneric()只是将原有的类转换为泛型,那么只需要setGeneric("names")就可以了;如果从头创建一个新的泛型,还需要提供一个名叫standardGeneric()的function,function里面就填泛型类的名字就可以:

setGeneric("myGeneric", function(x) {
 standardGeneric("myGeneric")
})

setMethod()利用泛型的名称、与该方法相关联的类以及实现该方法的函数实现参数调用和函数运行。

可以使用getGenerics()获得所有S4泛型的列表;使用getClasses()获得所有S4类的列表;可以使用showMethods()列出所有S4方法。

S4的泛型函数的定义和调用

该代码参考(copy)自R的极客理想中的R语言基于S4的面向对象编程,全文用深入浅出的方式介绍了S4泛型函数。

# 定义几何图形类
setClass("geometric_figure", slots = list(name = "character",
 shape = "character"))
​
# 定义圆形类, 利用prototype定义默认属性参数
setClass("Circle", slots = list(radius="numeric"),
 prototype = list(shape = "circle"),
 contains = "geometric_figure")
​
# 定义验证类,保证Circle 类中的属性正常
setValidity("Circle", function(object){
 if (object@radius <= 0)
 stop("The radius of circle is negative!")
})
​
# 面积计算 --------------------------------------------------------------------
​
# 定义计算面积的泛型函数接口
setGeneric("area",function(object){
 standardGeneric("area")
 })
​
# 定义面积的计算方法
setMethod("area","Circle",function(object){
 cat("The shape of", object@name, "is", object@shape,
 "\nThe Area of it is", pi*object@radius^2)
 })
​
​
# 周长计算 --------------------------------------------------------------------
​
# 定义计算周长的泛型函数接口
setGeneric("circum",function(object) standardGeneric("circum"))
​
# 定义计算周长的方法
 setMethod("circum","Circle",function(object){
 cat("The circumference of", object@name, "is",
 2*pi*object@radius)
 })
​
​
​
# 创建圆形实例
c <-new("Circle", name="c", radius=5)
# 函数调用 --------------------------------------------------------------------
area(c)
circum(c)

上述过程已经创建了几何图形(geometric_figure)类和面积|周长的泛型函数接口。如果想要计算其他图形,如椭圆、正方形、矩形,三角形、梯形等的周长和面积,只需要通过setClass()定义好继承类,然后再利用setMethod()定义好计算方法,最后就可以通过area()或者circum调用相关函数进行计算,自此就实现了接口和调用分离的S4函数调用全程。

参考资料

除了上面的连接,主要还参考了Advanced R programming - by Hadley Wickham

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容