R对象:S4——《A (Not So) Short Introduction to S4》学习笔记

S4类

1.基于对象的编程

1.1 定义

## 定义S4类,以下两者等价
setClass("BMI", representation(weight="numeric", size="numeric"))
## representation函数定义slot
setClass("BMI", slots=c(weight="numeric", size="numeric")) #
## 定义方法
setMethod(f, signature, definition) # f为泛型函数名,signature指定对象类名,definition定义方法
## 定义S4对象
new( "BMI", weight=1,size=1 )

1.2 检查

S4类特点:

  1. 类型检查
  2. 有效性检查设置
setValidity("BMI", function(object) {
    if (object@size < 0) {
        return("negative Size")
    } else {
        return(TRUE)
    }
})
## 若size属性<0会报错,报错信息为:negative Size

1.3 继承

setClass("BMIplus",representation(sex="character"),contains="BMI")
## contains定义继承,继承父类的属性

此外,可封装

S4对象包括:类(class)、变量(slot)、方法(methods)
slots定义类后,不能变更
methods操作:创建、校验(定义规则)、属性操作、其他

2. 面向对象编程基础

2.1 类的声明

Object@slot <-获取属性并赋值,应避免此类赋值,此外attr、attributes可用可任意赋值跳过检查,应避免
setClass(Class= , slots= , prototype= )prototype定义默认值,可不定义,系统默认空
removeClass()移除类,但相关方法并不移除

查看对象的函数方法:

  • slotNames()查看slots的名
  • getSlots()查看slots的名与类
  • getClass()查看slots的名与类以及相关的继承

2.2 方法

2.2.1 setmethod()定义已存在的泛型函数

setMethod( f="plot",              #f为已定义好的泛型函数名
                signature="BMI",   #S4类名
                definition=function(x,y...){...}  #参数应保持与泛型函数一致
                )

show()方法为展示对象时自动运行的函数,例如new("BMI")A自动引发此函数

2.2.2 setGeneric()定义新的S4泛型函数

setGeneric (
   name= "countMissing",                        
   def=function(object){standardGeneric("countMissing")}  #指派S4泛型函数
   )
## 可用setMethod定义对应类的方法
lockBinding("countMissing",.GlobalEnv)  #锁定方法,禁止修改

2.2.3 查看方法

showMethods(class= )查看类的方法
getMethod(f="plot",signature="Trajectories")查看对应类的方法代码
existsMethod(f="plot",signature="Trajectories")返回是否存在此函数

2.3 构造

2.3.1 校验

setClass(Class="Trajectories",
   representation(times="numeric",traj="matrix"),
   validity=function(object){  ##设置校验标准
       if(length(object@times)!=ncol(object@traj)){
       stop ("[Trajectories: validation] the number of temporal measurements does not correspond")                      ##设置报错信息,也可用return
               }else{
               return(TRUE)
               }})
 new("Trajectories")  #空的对象并不触发校验器
 ## setValidity也有此作用

2.3.2 初始化

## initialize方法在new()定义类的对象时初始化对象,
setMethod(f = "initialize", signature = "Trajectories", 
          definition = function(.Object,times,traj){   
          #.Object为类,后跟new()中应出现的参数,不一定为slots,可在此函数中定义规则计算slots使符合类的slots
            cat("~~~~~ Trajectories: initializator ~~~~~ \n")
            if (!missing(traj)) {
              colnames(traj) <- paste("T", times, sep = "")
              rownames(traj) <- paste("I", 1:nrow(traj), sep = "")
              .Object@times <- times
              .Object@traj <- traj
              validObject(.Object)  # 触发校验器
              return(.Object)
            }
          })

2.3.3 自建构造器

trajectories <- function(times, traj) {
  if (missing(times)) {     #未定义时自行定义
    times <- 1:ncol(traj)
  }
  new(Class = "Trajectories", times = times, traj = traj)
}
## 避免重复输入Class="Trajectories"参数
## 亦可在自定义函数中自定义slots的内容计算规则,自动调用校验器

由于初始化器定义了输入规则,构造新对象规则时难修改,且需定义触发校验器,推荐使用自建构造器取代初始化器

2.4 存储器

## 获取slot
## @也可,但由于可以随意赋值,危险!!!
setGeneric("getTraj",function(object){standardGeneric("getTraj")}) #设置获取泛型函数
setMethod("getTraj", "Trajectories", function(object) {       
    return(object@traj)
})

## 修改slot
setGeneric("setTimes<-",function(object,value){standardGeneric("setTimes<-")})
setMethod("setTimes<-","Trajectories",   ##也可用setReplaceMethod,此时不用加<-
          function(object,value){
            object@times <-value
            validObject(object)                ## 可触发校验器
            return(object)
          })
a=new("Trajectories")
setTimes(a)<-1:4     
## 若设置slot,需定义set<-函数族,这样才可覆盖原对象
## 若定义函数setTimes,则value参数应在()内,并不改变原对象

## "[" "[<-"虽然在S4不可用,但在定义后可用
setMethod(
   f= "[",
   signature="Trajectories",
   definition=function(x,i,j,drop){
     if(i=="times"){return(x@times)}else {}
     if(i=="traj"){return(x@traj)}else {}
     }
   )

3. 面向对象编程进阶

3.1 方法参数

  • signature
    signature=c(x="numeric",y="character")定义包含多类的参数方法,注意类的个数应与参数个数对应
    signature中的参数类未定义, 则默认为“ANY”类,任一均试用。
    此外还可定义signature中的参数类为"missing",此时当此参数缺省时试用

3.2 继承

继承于多个类是危险的。
setClass("son",slots=c(),contain="father"定义继承,父继承的slots可不写;
unclass()可以展示所有slots包括继承的
hasMethod("initialize","TrajPartitioned")查找方法与existsMethod不同的是,会查找父类方法
selectMethod("initialize","TrajPartitioned")显示代码方法,与getMethod不同的是会查找父类方法

setMethod(
    f="print",
    signature="TrajPartitioned",
    definition=function(x,...){
      callNextMethod()                  ##触发父类继承方法
      cat("the object also contains",length(x@listPartitions),"partition")
      return(invisible())
    }
  )

callNextMetod比较局限,因为其完全依赖于父类对象

as(object, Class)将object转化为继承的Class,此转化后的对象可用转化后对象的方法
is(object,class2)判断object是否属于class2,包含继承关系
as(objectSon,"ClassFather") <- objectFather将子类转化为父类并且对应的slots赋值为父类对象的slots

setIs(class1="son", class2="father",   ##setIs定义超类,可将不继承的类定义继承
    test=function(from){if()return(TRUE)},  ##定义条件超类,此情况并不继承超类方法
    coerce=function(from,to){to=new(to)
        return(to)},      ## coerce定义转化方法,用于as
    replace=function(from,value){return(from)})  ## replace定义替换,用于as<- value
    
## from对应class1,to对应class2
setAs(from, to, 
    def=function(from,to){to=new(to);return(to)},              ##定义as
    replace=function(from, value){return(from)})  ## 定义as<-
## setAs与setIs的区别:setIs运行后自动转化为超类,setAs需要定义def后才可在as对象时转换,且并未定义超类关系

虚拟类

定义方法:

setClass(
     Class="PartitionFather",
     slots=representation(nbGroups="numeric"),
     contains = "VIRTUAL" )
 ##或
 setClass(
     Class="PartitionFather",
     representation=representation(nbGroups="numeric","VIRTUAL"))
 ## 注:representation()函数中,named参数表示slot,unamed表示class

虚拟类不能创建对象,但可创建继承,继承类的对象可继承虚拟类的方法

内部修改

在定义方法setMethod中,加入:
nameObject<-deparse(substitute(.Object))
assign(nameObject,.Object,envir=parent.frame())
可直接修改参数.Object于父环境中

编程注意事项

if 后无论是否有else的必要一定要加else,否则多个if会混淆
定义方法时最好先构造函数测试顺利后再构建
每一类包括其方法单独一个R文档

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

推荐阅读更多精彩内容

  • 要点: 函数式编程:注意不是“函数编程”,多了一个“式” 模块:如何使用模块 面向对象编程:面向对象的概念、属性、...
    victorsungo阅读 1,502评论 0 6
  • 写在之前 因为简书字数限制,完整版地址:https://www.zybuluo.com/hainingwyx/no...
    hainingwyx阅读 13,911评论 0 41
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,124评论 0 21
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,755评论 0 8
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,563评论 0 5