Jeason
1105字约4分钟
2024-09-23
在R语言中,基于S3对象的面向对象编程,是一种基于泛型函数的实现方式。泛型函数是一种特殊的函数,根据传入对象的类型决定调用那个具体的方法。基于S3对象实现面向对象编程,不同其他语言的面型对象编程,是一种动态函数调用的模拟实现。S3对象被广泛应用于R的早期的开发包中。
S3对象的创建非常简单,只需要把相应的属性添加到class
即可,主要通过class
来区分S3对象
x <- 1
attr(x,'class') <- 'foo'
## [1] 1
## attr(,"class")
## [1] "foo"
attr(x,"class")
## [1] "foo"
class(x)
## [1] "foo"
通过structure()
函数也可以创建S3对象
y <- structure(2,class="foo")
y
## [1] 2
## attr(,"class")
## [1] "foo"
attr(y,"class")
## [1] "foo"
class(x)
## [1] "foo"
创建一个多类型的S3对象,S3独享没有明确结构关系,一个S3对象可以有多个类型,S3对象的class
属性可以是一个,也可以包括多种类型
x <- 1
attr(x,"class") <- c("foo","bar")
class(x)
## [1] "foo" "bar"
对于S3对象的使用,通常用UseMethod()
函数来定义一个泛型函数的名称,通过传入参数的class
属性,来确定方法调用。
简单的可以通过泛型函数完成以下任务:
UseMethod()
定义teacher
泛型函数teacher.xxx
的语法格式定义teacher
对象的行为teacher.default
是默认行为
# 用UseMethod()定义teacher泛型函数
teacher <- function(x,...) UseMethod("teacher")
# 定义teacher内部函数
teacher.default <- function(x,...) print("你不是teacher")
teacher.correcting <- function(x,...) print("批改作业")
方法调用通过传入参数的class
属性,来确定不同方法调用
a
,并设置a
的class
属性为correcting
a
传入到teacher
泛型函数中teacher.correcting()
函数的行为被调用a <- "teacher"
attr(a,"class") <- 'correcting'
teacher(a)
[1] “批改作业”
teacher()
[1] "你不是teacher"
假如想要对内部函数添加更多的参数用于完成更特定的任务,则可在内部函数中直接添加特定的参数
teacher.assignment <- function(x, name = '小明',...) print(paste0("布置作业给", name))
a <- "teacher"
attr(a,"class") <- 'assignment'
teacher(a, '小明')
[1] "布置作业给小明"
注意
...
参数,可以避免warning出现当我们使用S3进行面向对象封装后,可以使用methods()
函数来查看S3对象中的定义的内部行为函数。
# 查看teacher对象
teacher
function(x,...) Usemethod("teacher")
# 查看teacher对象的内部函数
methods(teacher)
[1] teacher.assignment teacher.correcting teacher.default teacher.lecture
#通过methods()的generic.function参数,来匹配泛型函数名字
methods(generic.function = predict)
[1] predict.ar* ......
通过methods()
的class
参数,来匹配类的名字
methods(class=lm)
[1]add1.lm* ......
用getAnywhere()
函数,查看所有函数
#查看teacher.lecture函数
>getAnywhere(teacher.correcting)
使用getS3method()
函数,也同样可以查看不可见的函数
# getS3method()函数查找predict.ppr
getS3method("predict","ppr")
S3独享有一种非常简单的继承方式,用NextMethod()
函数来实现。
node <- function(x) UseMethod("node",x)
node.default <- function(x) "Default node"
#father函数
node.father <- function(x) c("father")
# son函数,通过NextMethod()函数继承father函数
node.son <- function(x) c('son',NextMethod())
#定义n1
n1 <- structure(1,class=c("father"))
# 在node函数中传入n1,执行node.father()函数
node(n1)
[1] "father"
# 定义n2,设置class属性为两个
n2 <- structure(1,class=c("son","father"))
# 在node函数中传入n2,执行node.son()函数和node.father()函数
node(n2)
[1] "son" "father"
通过对node()
函数传入n2
的参数,node.son()
先被执行,然后通过NextMethod()
函数继续执行了node.father()
函数。这样其实就模拟了,子函数调用父函数的过程,实现了面向对象编程中的继承。