R语言面向对象编程:S3和R6

1、基于S3的面向对象编程数据库

基于S3的面向对象编程是一种基于泛型函数(generic function)的实现方式。编程

1.S3函数的建立函数

S3对象组成:generic(generic FUN)+method(generic.class FUN)spa

泛型函数(generic)建立示例:code

get_n_elements <- function(x,...)
{
UseMethod("get_n_elements")
}

一般用UseMethod()函数定义一个泛型函数的名称,经过传入参数的class属性,来肯定对应的方法调用。对象

method(generic.class)函数,建立示例:blog

 # Create a data.frame method for get_n_elements
get_n_elements.data.frame <- function(x, ...) 
{
nrow(x) * ncol(x) # or prod(dim(x))
}

# Create a default method for get_n_elements
#在使用UseMethod调用时,先在methods中寻找对应class,若是都没有找到,则会调用#default方法。
get_n_elements.default <- function(x,...)
{
length(unlist(x))
}

methods() 用于查找S3泛型函数中全部可用的methods。继承

调用pryr包中的is_s3_method() 能够验证函数是否S3方法。element

 

二、S3对象的传入参数有多个class属性的处理方法get

当变量x具备多个class属性,应按具体到通用的顺序来排列变量对应的class。

使用NextMethod()来调用methods

an_s3_method.some_class <- function(x, ...)
{
# Act on some_class, then
NextMethod("an_s3_method")
}

具体示例以下:

# Assign classes
class(kitty) <- c("cat","mammal","character")

what_am_i <- function(x,...)
{
   UseMethod("what_am_i")
}

# cat method
what_am_i.cat <- function(x)
{
  message("I'm a cat")
  NextMethod("what_am_i")
}

# mammal method
what_am_i.mammal <- function(x, ...)
{
  message("I'm a mammal")
  NextMethod("what_am_i")
}

# character method
what_am_i.character <- function(x, ...)
{
  message("I'm a character vector")
}

 

 

 

2、基于R6的面向对象编程

一、R6对象的建立

首先使用 R6Class() 建立一个class generator(也可叫factory)。

第一个参数是建立的对象的类的名字。
参数private为一个list,为对象保存数据域(data field),包含每一个元素的名字。
参数pubilc为一个list,为对象保存面向用户的函数或功能。

library(R6)
thing_factory <- R6Class( "Thing", private = list( a_field = "a value", another_field = 123 ), public = list( do_something = function(x, y, z) { # Do something here } ) )

建立factory后,再调用new()来生成一个R6对象。new()无需定义,全部的factory都默认具备该方法。

a_thing <- thing_factory$new()

initialize()是一种特殊的公有方法(public method), 在R6对象建立时自动调用,用来设置私域(private field)值。

new()中的参数被传给initialize()。

# Add an initialize method
microwave_oven_factory <- R6Class(
"MicrowaveOven",
private = list(
power_rating_watts = 800,
door_is_open = FALSE
),
public = list(
cook = function(time_seconds) {
Sys.sleep(time_seconds)
print("Your food is cooked!")
},
open_door = function() {
private$door_is_open = TRUE
},
close_door = function() {
private$door_is_open = FALSE
},
# Add initialize() method here
initialize = function(power_rating_watts,door_is_open){
if(!missing(power_rating_watts)){
private$power_rating_watts<-power_rating_watts
}
if(!missing(door_is_open)){
private$door_is_open<-door_is_open
}
}
)
)

# Make a microwave
a_microwave_oven <- microwave_oven_factory$new(
power_rating_watts = 650,
door_is_open = TRUE
)

  

2.访问和设置私有域

private组件中的数据对用户隐藏,这是封装的原理。使用private$前缀能够访问私域(private field)。

存在active组件中的active binding(行为像变量的函数),能够获取和设置私有数据域。

Active bindings是R6中一种特殊的函数调用方式,把对函数的访问表现为对属性的访问,属于公有组件。

thing_factory <- R6Class(
  "Thing",
  private = list(
    ..a_field = "a value"
  ),
  active = list(
    a_field = function(value) {
      if(missing(value)) {
        private$..a_field
      } else {
        assert_is_a_string(value) # or another assertion
        private$..a_field <- value
      }
    }
  )
)

 将active binding做为数据变量而不是函数进行调用。

 a_thing <- thing_factory$new()

a_thing$a_field # not a_thing$a_field()

 

三、R6的继承 

(1)子类的建立

继承将一个类(class)的功能复制到另外一个。可在R6Class()中用inherit参数来建立子类。

child_class_factory <- R6Class(
"ChildClass",
inherit = parent_class_factory
)

子类能够添加公有方法来扩展父类的功能,并在公有方法中使用前缀self$调用其余公有方法。

子类也可定义与父类相同的方法名称来重写该方法,用于扩展父类的功能,子类使用前缀super$访问父类的公有方法。

(2)跨级访问

R6类默认只能使用直接父类的功能。为了跨级访问,中间类(intermediate classes)首先须要定义一个active binding来显示父类,形式以下:

active = list(super_ = function() super)

而后能够跨级使用方法parent_method <- super$method()grand_parent_method <- super$super_$method(great_grand_parent_method <- super$super_$super_$method()


(3)共享域


大部分类型的R变量是经过值复制,意思是当复制一个变量时 ,新的变量具备本身的复制值,改变其中一个变量不会影响另一个。
环境变量(Environments)比较特殊,经过地址传送来复制(by reference),所以全部的复制品都是等同的,改变其中一个就会改变其余变量。
R6类可利用环境的地址传送(by reference)复制行为在对象之间共享区域,定义一个名为shared的私域,方式以下:
建立一个新的环境,指定该环境的任意共享域,可经过active bindings访问。

工做方式与其余active bindings相同,但需使用private$shared$前缀来找回(retrieve)这些区域
R6Class(
"Thing",
private = list(
shared = {
e <- new.env()
e$a_shared_field <- 123
e
}
),
active = list(
a_shared_field = function(value) {
if(missing(value)) {
private$shared$a_shared_field
} else {
private$shared$a_shared_field <- value
}
}
)
)

 

(4)R6对象的复制 

R6对象使用与环境变量相同的地址传送复制方式,若是使用<- 符号复制R6对象,原对象的变化会影响复制品。
a_reference_copy <- an_r6_object
R6 对象有一个自动生成的clone() 方法,用于值复制,使用clone()复制的对象的变化不影响其余对象。
a_value <- an_r6_object$clone()

当R6对象的一个或多个域包含另外一个R6对象时,默认clone() 经过地址传送复制该R6域。

若是值复制这些R6域,clone() 方法必须使用参数:deep = TRUE。
a_deep_copy <- an_r6_object$clone(deep = TRUE)


(5)R6对象的消除

当消除对象时,定义公有finalize()方法运行自定义代码。该方法通常用于关闭与数据库或文件的链接,或者消除例如改变全局选项(option)或者图形参数的反作用。

thing_factory <- R6Class(
"Thing",
public = list(
initialize = function(x, y, z) {
# do something
},
finalize = function() {
# undo something
# Print a message
"Disconnecting from the cooking times database."
# Disconnect from the database
dbDisconnect(private$conn) } ) ) 

当R的自动垃圾回收器(automated garbage collector)从存储中清除对象时调用finalize() 方法,可以使用gc()强制垃圾回收。

相关文章
相关标签/搜索