Description Usage Arguments Details The print method Portable and non-portable classes Cloning objects S3 details Examples
R6 objects are essentially environments, structured in a way that makes them look like an object in a more typical object-oriented language than R. They support public and private members, as well as inheritance across different packages.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
classname |
Name of the class. The class name is useful primarily for S3 method dispatch. |
public |
A list of public members, which can be functions (methods) and non-functions (fields). |
private |
An optional list of private members, which can be functions and non-functions. |
active |
An optional list of active binding functions. |
inherit |
A R6ClassGenerator object to inherit from; in other words, a
superclass. This is captured as an unevaluated expression which is
evaluated in |
lock_objects |
Should the environments of the generated objects be locked? If locked, new members can't be added to the objects. |
class |
Should a class attribute be added to the object? Default is
|
portable |
If |
lock_class |
If |
cloneable |
If |
parent_env |
An environment to use as the parent of newly-created objects. |
lock |
Deprecated as of version 2.1; use |
An R6 object consists of a public environment, and may also contain a private environment, as well as environments for superclasses. In one sense, the object and the public environment are the same; a reference to the object is identical to a reference to the public environment. But in another sense, the object also consists of the fields, methods, private environment and so on.
The active
argument is a list of active binding functions. These
functions take one argument. They look like regular variables, but when
accessed, a function is called with an optional argument. For example, if
obj$x2
is an active binding, then when accessed as obj$x2
, it
calls the x2()
function that was in the active
list, with no
arguments. However, if a value is assigned to it, as in obj$x2 <- 50
,
then the function is called with the right-side value as its argument, as in
x2(50)
. See makeActiveBinding
for more information.
If the public or private lists contain any items that have reference
semantics (for example, an environment), those items will be shared across
all instances of the class. To avoid this, add an entry for that item with a
NULL
initial value, and then in the initialize
method,
instantiate the object and assign it.
print
methodR6 object generators and R6 objects have a default print
method to
show them on the screen: they simply list the members and parameters (e.g.
lock_objects, portable, etc., see above) of the object.
The default print
method of R6 objects can be redefined, by
supplying a public print
method. (print
members that are not
functions are ignored.) This method is automatically called whenever the
object is printed, e.g. when the object's name is typed at the command
prompt, or when print(obj)
is called. It can also be called directly
via obj$print()
. All extra arguments from a print(obj, ...)
call are passed on to the obj$print(...)
method.
When R6 classes are portable (the default), they can be inherited across
packages without complication. However, when in portable mode, members must
be accessed with self
and private
, as in self$x
and
private$y
.
When used in non-portable mode, R6 classes behave more like reference
classes: inheritance across packages will not work well, and self
and private
are not necessary for accessing fields.
R6 objects have a method named clone
by default. To disable this,
use cloneable=FALSE
. Having the clone
method present will
slightly increase the memory footprint of R6 objects, but since the method
will be shared across all R6 objects, the memory use will be negligible.
By default, calling x$clone()
on an R6 object will result in a
shallow clone. That is, if any fields have reference semantics
(environments, R6, or reference class objects), they will not be copied;
instead, the clone object will have a field that simply refers to the same
object.
To make a deep copy, you can use x$clone(deep=TRUE)
. With this
option, any fields that are R6 objects will also be cloned; however,
environments and reference class objects will not be.
If you want different deep copying behavior, you can supply your own
private method called deep_clone
. This method will be called for
each field in the object, with two arguments: name
, which is the
name of the field, and value
, which is the value. Whatever the
method returns will be used as the value for the field in the new clone
object. You can write a deep_clone
method that makes copies of
specific fields, whether they are environments, R6 objects, or reference
class objects.
Normally the public environment will have two classes: the one supplied in
the classname
argument, and "R6"
. It is possible to get the
public environment with no classes, by using class=FALSE
. This will
result in faster access speeds by avoiding class-based dispatch of
$
. The benefit is negligible in most cases.
If a class is a subclass of another, the object will have as its classes
the classname
, the superclass's classname
, and "R6"
The primary difference in behavior when class=FALSE
is that, without
a class attribute, it won't be possible to use S3 methods with the objects.
So, for example, pretty printing (with print.R6Class
) won't be used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | # A queue ---------------------------------------------------------
Queue <- R6Class("Queue",
public = list(
initialize = function(...) {
for (item in list(...)) {
self$add(item)
}
},
add = function(x) {
private$queue <- c(private$queue, list(x))
invisible(self)
},
remove = function() {
if (private$length() == 0) return(NULL)
# Can use private$queue for explicit access
head <- private$queue[[1]]
private$queue <- private$queue[-1]
head
}
),
private = list(
queue = list(),
length = function() base::length(private$queue)
)
)
q <- Queue$new(5, 6, "foo")
# Add and remove items
q$add("something")
q$add("another thing")
q$add(17)
q$remove()
#> [1] 5
q$remove()
#> [1] 6
# Private members can't be accessed directly
q$queue
#> NULL
# q$length()
#> Error: attempt to apply non-function
# add() returns self, so it can be chained
q$add(10)$add(11)$add(12)
# remove() returns the value removed, so it's not chainable
q$remove()
#> [1] "foo"
q$remove()
#> [1] "something"
q$remove()
#> [1] "another thing"
q$remove()
#> [1] 17
# Active bindings -------------------------------------------------
Numbers <- R6Class("Numbers",
public = list(
x = 100
),
active = list(
x2 = function(value) {
if (missing(value)) return(self$x * 2)
else self$x <- value/2
},
rand = function() rnorm(1)
)
)
n <- Numbers$new()
n$x
#> [1] 100
n$x2
#> [1] 200
n$x2 <- 1000
n$x
#> [1] 500
# If the function takes no arguments, it's not possible to use it with <-:
n$rand
#> [1] 0.2648
n$rand
#> [1] 2.171
# n$rand <- 3
#> Error: unused argument (quote(3))
# Inheritance -----------------------------------------------------
# Note that this isn't very efficient - it's just for illustrating inheritance.
HistoryQueue <- R6Class("HistoryQueue",
inherit = Queue,
public = list(
show = function() {
cat("Next item is at index", private$head_idx + 1, "\n")
for (i in seq_along(private$queue)) {
cat(i, ": ", private$queue[[i]], "\n", sep = "")
}
},
remove = function() {
if (private$length() - private$head_idx == 0) return(NULL)
private$head_idx <<- private$head_idx + 1
private$queue[[private$head_idx]]
}
),
private = list(
head_idx = 0
)
)
hq <- HistoryQueue$new(5, 6, "foo")
hq$show()
#> Next item is at index 1
#> 1: 5
#> 2: 6
#> 3: foo
hq$remove()
#> [1] 5
hq$show()
#> Next item is at index 2
#> 1: 5
#> 2: 6
#> 3: foo
hq$remove()
#> [1] 6
# Calling superclass methods with super$ --------------------------
CountingQueue <- R6Class("CountingQueue",
inherit = Queue,
public = list(
add = function(x) {
private$total <<- private$total + 1
super$add(x)
},
get_total = function() private$total
),
private = list(
total = 0
)
)
cq <- CountingQueue$new("x", "y")
cq$get_total()
#> [1] 2
cq$add("z")
cq$remove()
#> [1] "x"
cq$remove()
#> [1] "y"
cq$get_total()
#> [1] 3
# Non-portable classes --------------------------------------------
# By default, R6 classes are portable, which means they can be inherited
# across different packages. Portable classes require using self$ and
# private$ to access members.
# When used in non-portable mode, members can be accessed without self$,
# and assignments can be made with <<-.
NP <- R6Class("NP",
portable = FALSE,
public = list(
x = NA,
getx = function() x,
setx = function(value) x <<- value
)
)
np <- NP$new()
np$setx(10)
np$getx()
#> [1] 10
# Setting new values ----------------------------------------------
# It is possible to add new members to the class after it has been created,
# by using the $set() method on the generator.
Simple <- R6Class("Simple",
public = list(
x = 1,
getx = function() self$x
)
)
Simple$set("public", "getx2", function() self$x*2)
# Use overwrite = TRUE to overwrite existing values
Simple$set("public", "x", 10, overwrite = TRUE)
s <- Simple$new()
s$x
s$getx2()
# Cloning objects -------------------------------------------------
a <- Queue$new(5, 6)
a$remove()
#> [1] 5
# Clone a. New object gets a's state.
b <- a$clone()
# Can add to each queue separately now.
a$add(10)
b$add(20)
a$remove()
#> [1] 6
a$remove()
#> [1] 10
b$remove()
#> [1] 6
b$remove()
#> [1] 20
# Deep clones -----------------------------------------------------
Simple <- R6Class("Simple",
public = list(
x = NULL,
initialize = function(val) self$x <- val
)
)
Cloner <- R6Class("Cloner",
public = list(
s = NULL,
y = 1,
initialize = function() self$s <- Simple$new(1)
)
)
a <- Cloner$new()
b <- a$clone()
c <- a$clone(deep = TRUE)
# Modify a
a$s$x <- 2
a$y <- 2
# b is a shallow clone. b$s is the same as a$s because they are R6 objects.
b$s$x
#> [1] 2
# But a$y and b$y are different, because y is just a value.
b$y
#> [1] 1
# c is a deep clone, so c$s is not the same as a$s.
c$s$x
#> [1] 1
c$y
#> [1] 1
# Deep clones with custom deep_clone method -----------------------
CustomCloner <- R6Class("CustomCloner",
public = list(
e = NULL,
s1 = NULL,
s2 = NULL,
s3 = NULL,
initialize = function() {
self$e <- new.env(parent = emptyenv())
self$e$x <- 1
self$s1 <- Simple$new(1)
self$s2 <- Simple$new(1)
self$s3 <- Simple$new(1)
}
),
private = list(
# When x$clone(deep=TRUE) is called, the deep_clone gets invoked once for
# each field, with the name and value.
deep_clone = function(name, value) {
if (name == "e") {
# e1 is an environment, so use this quick way of copying
list2env(as.list.environment(value, all.names = TRUE),
parent = emptyenv())
} else if (name %in% c("s1", "s2")) {
# s1 and s2 are R6 objects which we can clone
value$clone()
} else {
# For everything else, just return it. This results in a shallow
# copy of s3.
value
}
}
)
)
a <- CustomCloner$new()
b <- a$clone(deep = TRUE)
# Change some values in a's fields
a$e$x <- 2
a$s1$x <- 3
a$s2$x <- 4
a$s3$x <- 5
# b has copies of e, s1, and s2, but shares the same s3
b$e$x
#> [1] 1
b$s1$x
#> [1] 1
b$s2$x
#> [1] 1
b$s3$x
#> [1] 5
# Debugging -------------------------------------------------------
## Not run:
# This will enable debugging the getx() method for objects of the 'Simple'
# class that are instantiated in the future.
Simple$debug("getx")
s <- Simple$new()
s$getx()
# Disable debugging for future instances:
Simple$undebug("getx")
s <- Simple$new()
s$getx()
# To enable and disable debugging for a method in a single instance of an
# R6 object (this will not affect other objects):
s <- Simple$new()
debug(s$getx)
s$getx()
undebug(s$getx)
## End(Not run)
|
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.