S4 classes: arguments passed to new () are not in their slots

I am creating an R package with S4 classes and I am having problems with the new function. I have a class called Configs

 setClass("Configs", slots = list( burnin = "numeric", chains = "numeric", features = "numeric", iterations = "numeric", mphtol = "numeric", samples = "numeric", seed = "numeric", thin = "numeric", verbose = "numeric" ), prototype = list( burnin = 0, chains = 2, features = 5, iterations = 5, mphtol = 1e-4, samples = 3, seed = sample(1e6, 1), thin = 0, verbose = 0 ) ) 

and when I load only this part into my global environment, I can create a new Configs object with slots other than the default values.

 > new("Configs", features = 1000) An object of class "Configs" Slot "burnin": [1] 0 Slot "chains": [1] 2 Slot "features": [1] 1000 Slot "iterations": [1] 5 Slot "mphtol": [1] 1e-04 Slot "samples": [1] 3 Slot "seed": [1] 437211 Slot "thin": [1] 0 Slot "verbose": [1] 0 

However, when I install the whole package, load it into a new environment and run new("Configs", features = 1000) , I get features from 5. Why doesn't new() put the values ​​in the slots anymore?

My package passed the R CMD check without any errors, warnings or notes. Here is my session information.

 > sessionInfo() R version 3.2.0 (2015-04-16) Platform: x86_64-unknown-linux-gnu (64-bit) Running under: CentOS release 6.6 (Final) locale: [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 [7] LC_PAPER=en_US.UTF-8 LC_NAME=C [9] LC_ADDRESS=C LC_TELEPHONE=C [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] heterosis_0.0 pracma_1.8.3 MCMCpack_1.3-3 MASS_7.3-40 coda_0.17-1 loaded via a namespace (and not attached): [1] tools_3.2.0 grid_3.2.0 lattice_0.20-31 

Edit: I get it, but I'm still not satisfied.

Turns out my initialize function was causing problems.

 setMethod("initialize", "Configs", function(.Object, ...){ # .Object = new("Configs", ...) validObject(.Object) return(.Object) }) 

When I delete it, new places things in slots again. I'm glad I found the problem, but I do not want to completely disable the initialization function. I want a convenient way to call validObject and do another error checking, and initialize seems like the right place for this. And if I uncomment the commented line, I get infinite recursion. How to create a constructor without breaking new ?

+5
source share
1 answer

initialize() - binary assignment - initialization and construction of a copy. Usually it is better (also more informative for the user) to provide an explicit constructor

 .A = setClass("A", representation(x="numeric")) A = function(x=numeric(), ...) .A(x=x, ...) 

validOjbect() is called by default by the initialization method when object creation includes slot assignment, so there is no need to call it explicitly during your own initialization method (see below); maybe you will have

 .A = setClass("A", representation(x="numeric"), prototype=prototype(x=NA_integer_)) setValidity("A", function(object) { if (length( object@x ) != 1L) "'x' must be length 1" else TRUE }) A = function(x=NA_integer_, ...) ## signature is informative -- 'x' is integer(1), not just '...' ## coercion (eg, as.integer(), below) and other set-up new("A", x=as.integer(x), ...) 

from

 > A() An object of class "A" Slot "x": [1] NA > A(x=1) An object of class "A" Slot "x": [1] 1 > A(x=1:2) Error in validObject(.Object) : invalid class "A" object: 'x' must be length 1 

An important caveat is that the validity method is not called when there are no slots initialized by the user, so prototype() must be defined to create a valid object (check this with validObject(new("A")) .

For your question, the credibility function is the right place to "check for other errors." It is very difficult to write the correct initialization method, but something closer to the correct one:

 .B = setClass("B", representation(x="numeric", y="numeric"), prototype=prototype(x=NA_integer_, y=NA_real_)) setMethod("initialize", "B", function(.Object, ..., x=.Object@x , y=.Object@y ) { ## pre-processing, then invoke 'next' initialize() method ## base initialize() creates the object then calls validObject() ## so no need for explicit test of validity .Object <- callNextMethod(.Object, ..., x=x, y=y) ## post-processing .Object }) 

This weird construction allows initialize() to continue to act like a copy constructor.

 > b = new("B", x=1, y=2) # constructor > initialize(b, x=2) # copy-constructor An object of class "B" Slot "x": [1] 2 Slot "y": [1] 2 

which is important when inheriting a class. But, as you can see, this is quite complicated - after all, it is very difficult and rarely worth the effort to get initialize() correct.

Note that we did not completely fulfill the initialize() contract,

 setClass("C", representation(x="numeric", y="numeric")) # default initialize() 

which actually acts as a copy constructor when called with new()

 > c = new("C", x=1, y=2) > new("C", c, x=2) An object of class "C" Slot "x": [1] 2 Slot "y": [1] 2 

against the copy construct for implementing B

 > b = new("B", x=1, y=2) > new("B", b, x=2) An object of class "B" Slot "x": [1] 2 Slot "y": [1] NA 
+6
source

All Articles