Methods_for_Nongenerics
位于 methods
包(package)。 说明
在编写方法时R包中,这些方法通常应用于该包中不通用的函数(在另一个包中);也就是说,该函数在其自己的包中没有正式的方法,尽管它可能有 S3 方法。在这种情况下的编程涉及一个额外的步骤,调用setGeneric()
声明该函数是在你的包中是通用的。
然后,对包中函数的调用将使用其中定义的所有方法或创建相同通用函数的任何其他加载包中定义的方法。同样,对这些包中的函数的调用将使用您的方法。
然而,原始版本仍然是非通用的。除特殊情况外,该包或使用该版本的其他包中的调用不会调度您的方法:
-
如果该函数是接受方法的原始函数之一,并且参数之一是 S4 对象(应该是这种情况),则内部 C 实现将调度方法。
-
如果该函数的其他版本调度 S3 方法,并且您的方法也注册为 S3 方法,则该方法通常会作为该 S3 方法调度。
-
否则,您将需要确保对该函数的所有调用都来自该函数是通用的包,也许可以通过将代码复制到您的包中来实现。
以下各节将讨论详细信息和根本原因。
通用和非通用调用
为包中的函数(任何函数)创建方法意味着调用该包中的函数将根据实际参数选择方法。但是,如果该函数最初是另一个包中的非泛型函数,则从该包调用该函数将不会调度方法。此外,来自导入非通用版本的任何第三个包的调用也不会调度方法。本节讨论原因以及如何处理后果。
原因很简单R命名空间机制及其在评估函数调用中的作用。当需要在对某个包中的函数的调用中评估名称(例如函数的名称)时,评估器首先在调用的框架中查找,然后在包的命名空间中查找,然后在导入中查找那个包。
为包中的函数定义方法可确保对该包中的函数的调用将选择这些方法,因为该函数的通用版本是在命名空间中创建的。同样,来自另一个具有或导入通用版本的包的调用将选择方法。由于通用版本相同,因此所有方法在所有这些包中都可用。
但是,来自任何导入旧版本或仅从搜索列表中选择它的包的调用通常不会选择方法。
举个例子,考虑基础包中的函数data.frame()
。该函数将任意数量的对象作为参数,并尝试将它们作为变量组合到 DataFrame 对象中。它通过为每个对象调用同样在基础包中的 as.data.frame()
来实现此目的。
合理的目标是通过定义 as.data.frame()
的方法来扩展可包含在 DataFrame 中的对象类。但是对 data.frame()
的调用仍将使用基础包中该函数的版本,该版本继续调用该包中的非通用 as.data.frame()
。
发生的事情的细节以及处理它的选项取决于函数的形式:原始函数;调度 S3 方法的函数;或一个普通的R函数。
原始函数并不实际R函数对象。它们直接进入内部 C 代码。然而,其中一些已被实现来识别方法。这些函数从内部 C 代码调度 S4 和 S3 方法。 S3 或 S4 都没有明确的通用函数。如果第一个参数或二元运算符的任一参数是 S4 对象,则内部代码将查找 S4 方法。如果没有找到S4方法,则搜索S3方法。因此,只要定义了相关的类,为这些函数定义方法就可以工作,情况应该始终如此。
函数通过调用 UseMethod()
调度 S3 方法,无论第一个参数是否是 S4 对象,它都不会查找形式方法。这适用于上面的 as.data.frame()
示例。要在这种情况下调用方法,如果可能的话,您的包还必须将该方法定义为 S3 方法。请参阅“S3 “Generic” 函数”部分。
在第三种可能性中,函数的定义不需要任何方法。例如,基础包具有许多计算矩阵参数的数值分解的函数。有些,例如 chol()
和 qr()
是为了调度 S3 方法而实现的;其他的,例如svd()
,直接作为特定计算来实现。可以直接编写和调用后一个函数的通用版本来定义形式方法,但不导入此通用版本的另一个包中的代码不会调度此类方法。
在这种情况下,您需要在对提供应分派方法的参数的函数的所有间接调用中使用通用版本。这可能需要提供新函数来分派方法,然后调用它们替换的函数。例如,如果 S3 方法不适用于 as.data.frame()
,则可以调用一个函数,该函数将通用版本应用于其所有参数,然后调用 data.frame()
作为该函数的替换。如果所有其他方法都失败,则可能需要复制相关函数,以便他们找到通用版本。
S3“Generic”函数
S3 方法调度查看第一个参数的类。 S3 方法是普通函数,具有与通用函数相同的参数。 S3 方法的 “signature” 由该方法分配的名称来标识,该名称由通用函数的名称、"."
和类的名称组成。有关详细信息,请参阅UseMethod
。
要实现与 S4 类对应的这些函数之一的方法,有两种可能性:S4 方法或具有 S4 类名称的 S3 方法。仅当预期签名具有第一个参数且没有其他参数时,S3 方法才可行。在这种情况下,推荐的方法是定义 S3 方法,并提供与 S4 方法的定义相同的函数。如果 S3 通用函数是 f3(x, ...)
并且新方法的 S4 类是 "myClass"
:
f3.myClass <- function(x, ...) { ..... }
setMethod("f3", "myClass", f3.myClass)
定义这两种方法通常可以确保对原始函数的所有调用都将调度预期的方法。使用该函数的原始版本不会从其他包中单独调用 S4 方法。另一方面,如果存在任何符合条件的非默认 S4 方法,则不会单独调用 S3 方法。
S4和S3方法选择被设计为尽可能遵循兼容的继承规则。 S3 类可用于任何 S4 方法选择,前提是 S3 类已通过调用 setOldClass
注册,并且该调用指定了正确的 S3 继承模式。 S4类可用于任何S3方法选择;当检测到 S4 对象时,S3 方法选择使用 extends(class(x))
的内容作为 S3 继承的等效内容(继承在第一次调用后被缓存)。
现有的 S3 方法可能无法按照 S4 子类所需的方式运行,在这种情况下,asS3
和 S3Part
等实用程序可能很有用。如果S3方法在S4对象上失败,则可以传递asS3(x)
;如果 S3 方法返回的对象需要合并到 S4 对象中,S3Part
的替换函数可能会很有用。
例子
## A class that extends a registered S3 class inherits that class' S3
## methods.
setClass("myFrame", contains = "data.frame",
slots = c(timestamps = "POSIXt"))
df1 <- data.frame(x = 1:10, y = rnorm(10), z = sample(letters,10))
mydf1 <- new("myFrame", df1, timestamps = Sys.time())
## "myFrame" objects inherit "data.frame" S3 methods; e.g., for `[`
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ] # a data frame object (with extra attributes)
## IGNORE_RDIFF_END
## a method explicitly for "myFrame" class
setMethod("[",
signature(x = "myFrame"),
function (x, i, j, ..., drop = TRUE)
{
S3Part(x) <- callNextMethod()
x@timestamps <- c(Sys.time(), as.POSIXct(x@timestamps))
x
}
)
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ]
## IGNORE_RDIFF_END
setClass("myDateTime", contains = "POSIXt")
now <- Sys.time() # class(now) is c("POSIXct", "POSIXt")
nowLt <- as.POSIXlt(now)# class(nowLt) is c("POSIXlt", "POSIXt")
mCt <- new("myDateTime", now)
mLt <- new("myDateTime", nowLt)
## S3 methods for an S4 object will be selected using S4 inheritance
## Objects mCt and mLt have different S3Class() values, but this is
## not used.
f3 <- function(x)UseMethod("f3") # an S3 generic to illustrate inheritance
f3.POSIXct <- function(x) "The POSIXct result"
f3.POSIXlt <- function(x) "The POSIXlt result"
f3.POSIXt <- function(x) "The POSIXt result"
stopifnot(identical(f3(mCt), f3.POSIXt(mCt)))
stopifnot(identical(f3(mLt), f3.POSIXt(mLt)))
## An S4 object selects S3 methods according to its S4 "inheritance"
setClass("classA", contains = "numeric",
slots = c(realData = "numeric"))
Math.classA <- function(x) { (getFunction(.Generic))(x@realData) }
setMethod("Math", "classA", Math.classA)
x <- new("classA", log(1:10), realData = 1:10)
stopifnot(identical(abs(x), 1:10))
setClass("classB", contains = "classA")
y <- new("classB", x)
stopifnot(identical(abs(y), abs(x))) # (version 2.9.0 or earlier fails here)
## an S3 generic: just for demonstration purposes
f3 <- function(x, ...) UseMethod("f3")
f3.default <- function(x, ...) "Default f3"
## S3 method (only) for classA
f3.classA <- function(x, ...) "Class classA for f3"
## S3 and S4 method for numeric
f3.numeric <- function(x, ...) "Class numeric for f3"
setMethod("f3", "numeric", f3.numeric)
## The S3 method for classA and the closest inherited S3 method for classB
## are not found.
f3(x); f3(y) # both choose "numeric" method
## to obtain the natural inheritance, set identical S3 and S4 methods
setMethod("f3", "classA", f3.classA)
f3(x); f3(y) # now both choose "classA" method
## Need to define an S3 as well as S4 method to use on an S3 object
## or if called from a package without the S4 generic
MathFun <- function(x) { # a smarter "data.frame" method for Math group
for (i in seq_len(ncol(x))[sapply(x, is.numeric)])
x[, i] <- (getFunction(.Generic))(x[, i])
x
}
setMethod("Math", "data.frame", MathFun)
## S4 method works for an S4 class containing data.frame,
## but not for data.frame objects (not S4 objects)
try(logIris <- log(iris)) #gets an error from the old method
## Define an S3 method with the same computation
Math.data.frame <- MathFun
logIris <- log(iris)
参考
Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)
也可以看看
Methods_for_S3 用于建议实现适用于 S3 和 S4 调度的方法。
相关用法
- R MethodsList 方法列表对象
- R MethodSupport 方法的附加(支持)函数
- R as 强制对象属于某个类
- R language-class 表示未评估语言对象的类
- R className 类名包含对应的包
- R BasicClasses 基本数据类型对应的类
- R callGeneric 从方法调用当前通用函数
- R findClass 查找类定义
- R setOldClass 注册旧式 (S3) 类和继承
- R ReferenceClasses 具有按引用处理的字段的对象(OOP 样式)
- R setGroupGeneric 创建函数的组通用版本
- R StructureClasses 基本结构对应的类
- R showMethods 显示指定函数或类的所有方法
- R getMethod 获取或测试方法的定义
- R slot 正式类对象中的槽
- R S4groupGeneric S4组通用函数
- R methodUtilities 用于方法和 S-Plus 兼容性的实用函数
- R getClass 获取类定义
- R evalSource 使用源文件中的函数定义,无需重新安装包
- R is 对象是来自类吗?
- R isSealedMethod 检查密封方法或类
- R cbind2 按列或行组合两个对象
- R GenericFunctions 管理通用函数的工具
- R dotsMethods 在方法签名中使用...
- R S3Part 包含 S3 类的 S4 类
注:本文由纯净天空筛选整理自R-devel大神的英文原创作品 Methods for Non-Generic Functions in Other Packages。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。