R语言 S4类

1 S4类

S4类是R语言中一种更为严格和复杂的面向对象编程方式。与更简单的S3类相比,S4类提供了更高的灵活性和严格性,适用于需要明确结构的复杂数据和应用场景。

更为详细的介绍:https://www.jianshu.com/p/dd181f0698a7

1.1 S4类的主要特点

  1. 严格的数据类型检查:在S4类中,每个对象的属性(称为“槽”)的类型必须明确指定。这意味着在创建对象时,R会强制检查类型是否匹配,从而减少类型相关的错误。
  2. 类的定义:S4类需要通过setClass()函数来显式定义。类定义中需要明确指定类的名称、包含的槽(属性)以及各个槽的类型。
  3. 方法的定义:S4类的方法是通过setMethod()函数定义的。与S3类不同,S4类的方法是基于签名(即输入参数的类型)来选择的,这使得方法的选择更加精准。
  4. 构造函数:可以使用new()函数来创建S4类的对象。new()函数会根据类的定义来检查并创建对象,确保每个槽都满足类定义中的要求。
  5. 继承关系:S4类支持多重继承,允许一个类继承多个父类的属性和方法。这使得S4类在复杂的数据结构建模中非常有用。

1.2 S4类的定义示例

下面是一个简单的S4类定义示例:

# 定义一个名为Person的S4类
setClass(
  Class = "Person",
  slots = list(
    name = "character",
    age = "numeric"
  )
)

# 定义一个构造方法
setMethod(
  "initialize", "Person",
  function(.Object, name, age) {
    .Object@name <- name
    .Object@age <- age
    return(.Object)
  }
)

# 创建一个Person对象
p <- new("Person", name = "John Doe", age = 30)

# 访问S4对象的槽
p@name
p@age

在这个示例中,Person类有两个槽:name(字符型)和age(数值型)。我们使用setClass()函数定义了这个类,并用new()函数创建了一个Person对象。访问S4对象的槽时,使用@符号。

 

 

REF

https://cloud.tencent.com.cn/developer/article/2448519

 

============================

 

定义类并创建对象

 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函数调用全程。
 

 
链接:https://www.jianshu.com/p/dd181f0698a7

 

============================

 


 

 

 

 

# 定义一个名为Person的S4类 setClass( Class = "Person", slots = list( name = "character", age = "numeric" ) ) # 定义一个构造方法 setMethod( "initialize", "Person", function(.Object, name, age) { .Object@name <- name .Object@age <- age return(.Object) } ) # 创建一个Person对象 p <- new("Person", name = "John Doe", age = 30) # 访问S4对象的槽 p@name p@age

posted @ 2024-10-20 15:55  emanlee  阅读(49)  评论(0编辑  收藏  举报