> langage et graphiques > Divers > Classes S4
Classes S4
Classes S4 : c'est la notion évoluée des classes sous R (voir classes S3 pour la comparaison entre les deux).
Définition d'une classe S4 :
setClass("Person",
representation(firstname = "character", lastname = "character"),
prototype(firstname = "Jean", lastname = "Dupond"),
validity = function(object) { ## object : nom reserve !
if (object@firstname == object@lastname)
## Person cannot have firstname equal to lastname
return(FALSE)
else
return(TRUE)
})
- "Person" est le nom de la classe.
- representation : donne les noms des slots (champs de l'objet) avec leur type.
- prototype : donne les valeurs par défaut.
- validity : fonction qui vérifie que les valeurs données lors de la construction de l'objet sont valides : renvoie TRUE si valides, FALSE sinon.
Attention, cela nécessite la librairie "methods" : mettre avant
library(methods).
Définition d'une classe dérivée :
setClass("PersonWithAge",
representation(age = "integer"),
contains = "Person",
validity = function(object) { ## object : nom reserve !
if (object@firstname == object@lastname)
## Person cannot have firstname equal to lastname
return(FALSE)
else
return(TRUE)
})
- l'attribut contains indique de quelle classe cette classe dérive.
- representation ne contient que les attributs supplémentaires par rapport à la classe de base.
Construction d'un objet :
Accès aux attributs de l'objet (slots) :
- obj@firstname : si l'objet s'appelle obj et le slot s'appelle firstname.
- ne pas utiliser ce type d'accès en dehors de la classe (équivalent à l'accès à des membres private dans d'autres langages objets).
Méthode initialize :
setMethod("initialize",
"Person",
function(.Object, firstname, lastname) {
print("initialize from Person called")
.Object@firstname <- paste("Mr", firstname, sep = " ")
.Object@lastname <- lastname
validObject(.Object) ## valide l'objet
return(.Object)
})
- permet de définir ce qu'il y a derrière le new (new appelle la méthode initialize si elle existe, sinon, elle appelle le initialize par défaut).
- un seul initialize par classe possible, et il peut prendre le nom des slots ou d'autres variables.
- le initialize par défaut appelle automatiquement validObject, alors qu'il faut l'appeler explicitement (si l'on veut) dans une méthode initialize redéfinie.
- .Object : nom réservé obligatoire.
Définition d'une méthode (qui ne modifie pas l'objet lui-même) :
- il faut d'abord définir une méthode générique de même nom que la méthode à définir si ce n'est pas déjà le cas :
setGeneric("getFullName", function(obj, separator) {
return(standardGeneric("getFullName"))
})
- puis, on définit la méthode elle-même :
setMethod("getFullName", "Person", function(obj, separator) {
return(paste(obj@firstname, obj@lastname, sep = separator))
})
Définition d'une méthode qui modifie un slot de l'objet :
- modification d'un objet pas possible en R. Quand on fait names(vect) <- c("a", "b"), on crée en fait un nouveau vecteur vect avec les noms indiqués et on remplace l'ancien vecteur par le nouveau créé.
- on définit d'abord la méthode générique qui fait de l'affectation :
setGeneric("setFirstName<-", function(obj, value) {
return(standardGeneric("setFirstName<-"))
})
-
setReplaceMethod("setFirstName", "Person", function(obj, value) {
obj@firstname <- value
return(obj)
})
Utilisation de méthodes d'indiçage :
- accès en lecture : les noms des variables x, i et j sont obligatoires, x étant l'objet, i étant l'index et j un 2ème index optionnel :
setMethod("[", "Person", function(x, i, j) {
if (i == "firstname")
return(x@firstname)
else if (i == "lastname")
return(x@lastname)
else
stop("Index not defined")
})
print(person["lastname"])
- accès en écriture
setReplaceMethod("[", "Person", function(x, i, j, value) {
if (i == "firstname")
x@firstname <- value
else if (i == "lastname")
x@lastname <- value
return(x)
})
person["lastname"] <- "Durand"
On peut définir une méthode générique sans définir précisémment tous les arguments :
setGeneric("getComposedName",
function(obj1, obj2, ...) {
return(standardGeneric("getComposedName"))
})
Méthode sur plusieurs objets et méthode qui dépend de la signature des objets donnés (choix exact de la méthode dépendra des objets donnés) :
setMethod("getComposedName",
c(obj1 = "Person", obj2 = "Person"),
function(obj1, obj2, separator) {
return(paste(obj1@lastname, obj2@lastname, sep = separator))
})
setMethod("getComposedName",
c(obj1 = "character", obj2 = "Person"),
function(obj1, obj2) {
return(paste(obj1, obj2@lastname))
})
Diverses méthodes pour récupérer des infos sur les classes et méthodes :
- getGenerics() : renvoie un vecteur des fonctions génériques disponibles.
- removeClass("PersonWithAge") : enlève la définition de la classe (mais ne détruit pas les définitions des méthodes qu'il faut alors aller détruire séparément).
- getSlots("PersonWithAge") : renvoie un vecteur dont les noms sont les noms des slots et les valeurs sont les noms des types des slots
- args(getFullName) : donne la liste des arguments de la méthode getFullName.
- showMethods(classes = c("PersonWithAge")) : donne la liste des méthodes disponibles pour la classe.
- showMethods("getFullName") : pour avoir la description des méthodes dont le nom est getFullName.
- existsMethod("getFullName", "PersonWithAge") : teste si la méthode getFullName existe pour la classe PersonWithAge, sans prendre en compte l'héritage.
- hasMethod("getFullName", "PersonWithAge") : teste si la méthode getFullName existe pour la classe PersonWithAge, en prenant en compte l'héritage.
- getMethods("getFullName") : renvoie la description des méthodes getFullName.
Copyright Aymeric Duclert
programmer en R, tutoriel R, graphes en R