R Language S3 Classes

The R programming language has several ways to write object oriented code, including list encapsulation, S3, S4, RC, and R6. In general, the best approach is to use the RC (“reference classes”) technique.


Somewhat surprisingly, there are very few what I consider good examples of how to write S3 code. By good I mean skipping unneeded chit-chat and showing example code.

So, here’s my yet-another-S3 example. I’ll do a Person class. My demo script starts:

# s3person.R
# 3.4.2

# S3 OOP
Person = function(ln="NONAME", a=0, ht=0) {
  this = list(
    lastName = ln,
    age = a,
    height = ht
  class(this) = append(class(this), "Person")

Here I define a Person class where the object will have a lastName, an age, and a height. The “this” is a variable to reference the object and isn’t a reserved word so I could have used “me” or “self”. Notice that an S3 class is a function definition with a bit of extra plumbing with “class” and “append” keywords. (Note: I’m using ‘=’ instead of the preferred arrow operator so my annoying blog software doesn’t go crazy trying to interpret as HTML).

The class doesn’t have any methods. Methods in S3 are optional but useful so next my demo script adds a display method:

display = function(obj) {
  UseMethod("display", obj)

display.Person = function(obj) {
  cat("Last name : ", obj$lastName, "\n")
  cat("Age       : ", obj$age, "\n")
  cat("Height    : ", obj$height, "\n")

Each method in S3 needs (at least) two functions. The first of the pair essentially registers the name (here, “display”) of the method. The second function contains the implementation code. Notice the wacky “methodName.className” pattern of display.Person(). That’s just the magic syntax to use.

Next the demo script adds a method to set the value of a Person lastName:

setLastName = function(obj, ln) {
  UseMethod("setLastName", obj)

setLastName.Person = function(obj, ln) {
  obj$lastName = ln

The pattern should be clear now — register a method name, code up the method implementation.

OK, now my demo script tests the Person class. First:

cat("\nBegin S3 Person class demo \n")

cat("Initializing a default Person, p1 \n") 
p1 = Person()  # default param values
cat("Person 'p1' is: \n")

cat("Initializing a Person object p2 \n")
p2 = Person("Barrow", 29, 68.5)
cat("Person 'p2' is: \n")

The output of this part of code will be:

(prompt) source("s3person.R")

Begin S3 Person class demo 
Initializing a default Person, p1 
Person 'p1' is: 
Last name :  NONAME 
Age       :  0 
Height    :  0 

Initializing a Person object p2 
Person 'p2' is: 
Last name :  Barrow 
Age       :  29 
Height    :  68.5

You create an S3 object just like calling a regular R function. Next the demo concludes by showing how to set and get fields:

cat("Setting p1 directly and with a setter() \n") 
p1 = setLastName(p1, "Ankers")
p1$age = 19
p1$height = 61.1
cat("Person 'p1' is now: \n")

cat("\nEnd demo \n")

Notice the calling pattern when changing an object is object = function(object, value(s)) because of R’s pass-by-value mechanism.

This entry was posted in R Language. Bookmark the permalink.