Movatterモバイル変換


[0]ホーム

URL:


Title:Encapsulated Classes with Reference Semantics
Version:2.6.1
Description:Creates classes with reference semantics, similar to R's built-in reference classes. Compared to reference classes, R6 classes are simpler and lighter-weight, and they are not built on S4 classes so they do not require the methods package. These classes allow public and private members, and they support inheritance, even when the classes are defined in different packages.
License:MIT + file LICENSE
URL:https://r6.r-lib.org,https://github.com/r-lib/R6
BugReports:https://github.com/r-lib/R6/issues
Depends:R (≥ 3.6)
Suggests:lobstr, testthat (≥ 3.0.0)
Config/Needs/website:tidyverse/tidytemplate, ggplot2, microbenchmark,scales
Config/testthat/edition:3
Encoding:UTF-8
RoxygenNote:7.3.2
NeedsCompilation:no
Packaged:2025-02-14 21:15:19 UTC; winston
Author:Winston Chang [aut, cre], Posit Software, PBC [cph, fnd]
Maintainer:Winston Chang <winston@posit.co>
Repository:CRAN
Date/Publication:2025-02-15 00:50:02 UTC

R6: Encapsulated Classes with Reference Semantics

Description

logo

Creates classes with reference semantics, similar to R's built-in reference classes. Compared to reference classes, R6 classes are simpler and lighter-weight, and they are not built on S4 classes so they do not require the methods package. These classes allow public and private members, and they support inheritance, even when the classes are defined in different packages.

Author(s)

Maintainer: Winston Changwinston@posit.co

Other contributors:

See Also

Useful links:


Create an R6 reference object generator

Description

R6 objects are essentially environments, structured in a way that makes themlook like an object in a more typical object-oriented language than R. Theysupport public and private members, as well as inheritance across differentpackages.

Usage

R6Class(  classname = NULL,  public = list(),  private = NULL,  active = NULL,  inherit = NULL,  lock_objects = TRUE,  class = TRUE,  portable = TRUE,  lock_class = FALSE,  cloneable = TRUE,  parent_env = parent.frame())

Arguments

classname

Name of the class. The class name is useful primarily for S3method dispatch.

public

A list of public members, which can be functions (methods) andnon-functions (fields).

private

An optional list of private members, which can be functionsand non-functions.

active

An optional list of active binding functions.

inherit

A R6ClassGenerator object to inherit from; in other words, asuperclass. This is captured as an unevaluated expression which isevaluated inparent_env each time an object is instantiated.

lock_objects

Should the environments of the generated objects belocked? If locked, new members can't be added to the objects.

class

Should a class attribute be added to the object? Default isTRUE. IfFALSE, the objects will simply look likeenvironments, which is what they are.

portable

IfTRUE (the default), this class will work withinheritance across different packages. Note that when this is enabled,fields and members must be accessed withself$x orprivate$x; they can't be accessed with justx.

lock_class

IfTRUE, it won't be possible to add more members tothe generator object with$set. IfFALSE (the default), thenit will be possible to add more members with$set. The methods$is_locked,$lock, and$unlock can be used to queryand change the locked state of the class.

cloneable

IfTRUE (the default), the generated objects willhave method named$clone, which makes a copy of the object.

parent_env

An environment to use as the parent of newly-createdobjects.

Details

An R6 object consists of a public environment, and may also contain a privateenvironment, as well as environments for superclasses. In one sense, theobject and the public environment are the same; a reference to the object isidentical to a reference to the public environment. But in another sense, theobject also consists of the fields, methods, private environment and so on.

Theactive argument is a list of active binding functions. Thesefunctions take one argument. They look like regular variables, but whenaccessed, a function is called with an optional argument. For example, ifobj$x2 is an active binding, then when accessed asobj$x2, itcalls thex2() function that was in theactive list, with noarguments. However, if a value is assigned to it, as inobj$x2 <- 50,then the function is called with the right-side value as its argument, as inx2(50). SeemakeActiveBinding for more information.

If the public or private lists contain any items that have referencesemantics (for example, an environment), those items will be shared acrossall instances of the class. To avoid this, add an entry for that item with aNULL initial value, and then in theinitialize method,instantiate the object and assign it.

Theprint method

R6 object generators and R6 objects have a defaultprint method toshow them on the screen: they simply list the members and parameters (e.g.lock_objects, portable, etc., see above) of the object.

The defaultprint method of R6 objects can be redefined, bysupplying a publicprint method. (print members that are notfunctions are ignored.) This method is automatically called whenever theobject is printed, e.g. when the object's name is typed at the commandprompt, or whenprint(obj) is called. It can also be called directlyviaobj$print(). All extra arguments from aprint(obj, ...)call are passed on to theobj$print(...) method.

Portable and non-portable classes

When R6 classes are portable (the default), they can be inherited acrosspackages without complication. However, when in portable mode, members mustbe accessed withself andprivate, as inself$x andprivate$y.

When used in non-portable mode, R6 classes behave more like referenceclasses: inheritance across packages will not work well, andselfandprivate are not necessary for accessing fields.

Cloning objects

R6 objects have a method namedclone by default. To disable this,usecloneable=FALSE. Having theclone method present willslightly increase the memory footprint of R6 objects, but since the methodwill be shared across all R6 objects, the memory use will be negligible.

By default, callingx$clone() on an R6 object will result in ashallow 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 sameobject.

To make a deep copy, you can usex$clone(deep=TRUE). With thisoption, 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 ownprivate method calleddeep_clone. This method will be called foreach field in the object, with two arguments:name, which is thename of the field, andvalue, which is the value. Whatever themethod returns will be used as the value for the field in the new cloneobject. You can write adeep_clone method that makes copies ofspecific fields, whether they are environments, R6 objects, or referenceclass objects.

S3 details

Normally the public environment will have two classes: the one supplied intheclassname argument, and"R6". It is possible to get thepublic environment with no classes, by usingclass=FALSE. This willresult 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 classestheclassname, the superclass'sclassname, and"R6"

The primary difference in behavior whenclass=FALSE is that, withouta class attribute, it won't be possible to use S3 methods with the objects.So, for example, pretty printing (withprint.R6Class) won't be used.

Examples

# 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 itemsq$add("something")q$add("another thing")q$add(17)q$remove()#> [1] 5q$remove()#> [1] 6# Private members can't be accessed directlyq$queue#> NULL# q$length()#> Error: attempt to apply non-function# add() returns self, so it can be chainedq$add(10)$add(11)$add(12)# remove() returns the value removed, so it's not chainableq$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] 100n$x2#> [1] 200n$x2 <- 1000n$x#> [1] 500# If the function takes no arguments, it's not possible to use it with <-:n$rand#> [1] 0.2648n$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: foohq$remove()#> [1] 5hq$show()#> Next item is at index 2#> 1: 5#> 2: 6#> 3: foohq$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] 2cq$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 valuesSimple$set("public", "x", 10, overwrite = TRUE)s <- Simple$new()s$xs$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] 6a$remove()#> [1] 10b$remove()#> [1] 6b$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 aa$s$x <- 2a$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] 1c$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 fieldsa$e$x <- 2a$s1$x <- 3a$s2$x <- 4a$s3$x <- 5# b has copies of e, s1, and s2, but shares the same s3b$e$x#> [1] 1b$s1$x#> [1] 1b$s2$x#> [1] 1b$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)

Create a list from an R6 object

Description

This returns a list of public members from the object. It simply callsas.list.environment.

Usage

## S3 method for class 'R6'as.list(x, ...)

Arguments

x

An R6 object.

...

Other arguments, which will be ignored.


Is an object an R6 Class Generator or Object?

Description

Checks for R6 class generators and R6 objects.

Usage

is.R6(x)is.R6Class(x)

Arguments

x

An object.

Value

A logical value.

Examples

class_generator <- R6Class()object <- class_generator$new()is.R6Class(class_generator)is.R6(class_generator)is.R6Class(object)is.R6(object)

[8]ページ先頭

©2009-2025 Movatter.jp