简体   繁体   English

如何在不加载包的情况下从另一个包扩展 S3 方法

[英]How to extend S3 method from another package without loading the package

I am developing a package which has the function forecast.myclass .我正在开发一个具有功能forecast.myclass的包。 I want that function to work nicely with forecast package.我希望该功能与forecast包很好地配合使用。 Ie when forecast package is loaded the code forecast(object) should call forecast.myclass from my package.即当forecast包被加载时,代码forecast(object)应该从我的包中调用forecast.myclass

Since I need only generic definition of forecast from the package forecast , and I do not use any other function from the package forecast I am reluctant to include it in the Depends.由于我只需要从包forecast中对forecast进行通用定义,并且我不使用包forecast中的任何其他函数,因此我不愿意将它包含在 Depends 中。 So I define the generic in my package in the following way:所以我通过以下方式在我的包中定义泛型:

##'
##' @export
forecast <- function(object,...) UseMethod("forecast") 

##' @rdname forecast.midas_r
##' @method forecast midas_r
##' @export
forecast.midas_r <- function(object,newdata=NULL,method=c("static","dynamic"),insample=get_estimation_sample(object),...) {

Now everything works as expected when package forecast is not loaded.现在,当未加载包forecast时,一切都按预期工作。 But when I load package forecast , then forecast.midas_r is not called, when doing forecast(object) where object is of class midas_r .但是,当我加载包forecast时,在执行 predict forecast(object)时不会调用forecast.midas_r包,其中object属于midas_r类。 How should I solve this problem?我应该如何解决这个问题?

I'm not sure there's an easy solution to this.我不确定是否有一个简单的解决方案。 As others have said it's probably easiest to use Depends to get around this, rather than redefining a generic method.正如其他人所说,使用Depends来解决这个问题可能最简单,而不是重新定义通用方法。

Here's a simple example which works for me.这是一个对我有用的简单示例。 It's largely the same as your solution, but declaring @export means you won't need to manually update the NAMESPACE file.它与您的解决方案基本相同,但声明@export意味着您无需手动更新NAMESPACE文件。

##' @name mean
##' @export mean.newClass
##' 
##' @method mean newClass
##'
##' @title mean for \code{newClass} object
##' @param x A \code{newClass} object
##' @param ... Additional arguments
##'
mean.newClass <- function(x, ...){
  stopifnot(class(x)=="newClass")
  return(42)
}

Then package.skeleton("newPkg") .然后package.skeleton("newPkg") Put file mean.R with the above contents in the directory /R of the package.将上面内容的文件mean.R放到​​包的/R目录下。

Ensure you're in the directory 1 level below, then确保您在下面的目录 1 级别,然后

roxygenize("newPkg", roxygen.dir="newPkg", copy.package=F, unlink.target=F)

Now现在

library(devtools)
dev_mode(on=TRUE) ### don't want to have to uninstall the package later
install_local("newPkg")
library(newPkg)
x <- c(1,2)
class(x) <- "newClass"
stopifnot(mean(x)==42)
stopifnot(mean(unclass(x))==1.5)

I realize mean is a function in base but I have tested this for modifying generic functions elsewhere to give them a new method, so it should extend to your more general case also.我意识到meanbase中的一个函数,但我已经测试了它以在其他地方修改通用函数以给它们一个新方法,所以它也应该扩展到你更一般的情况。

The problem here is that your definition of the forecast generic is masking the definition from the forecast package, and your method is associated with your generic rather than the forecast package generic;这里的问题是您对forecast泛型的定义掩盖了预测包中的定义,并且您的方法与您的泛型相关联,而不是与预测包泛型相关联; this is just a complicated instance of two packages defining functions of the same name.这只是定义同名函数的两个包的复杂实例。 The solution is to bite the bullet and Depend: on forecast, or when at the command line and both your package and forecast are attached fully resolve the function mypackage::forecast() , or Import: forecast but not make the forecast generic available to the end user except by having them require(forecast) (this might be appropriate if forecast functionality were somehow peripheral to your package, eg, plotting in 3D when plotting in 2D was sufficient).解决方案是咬紧牙关,依赖:在预测上,或者在命令行上并且您的包和预测都附加了完全解析函数mypackage::forecast()或 Import: forecast 但不使预测通用最终用户,除非让他们require(forecast) (如果forecast功能在某种程度上是您的包的外围设备,这可能是合适的,例如,当以 2D 绘图时以 3D 绘图就足够了)。

For what it's worth, an S4 method in PkgB defined and exported on an imported S4 generic from PkgA implicitly exposes the S4 generic to the user, so the generic is available even if Imports: PkgA is specified in the DESCRIPTION file of PkgB.值得一提的是,PkgB 中的 S4 方法在从 PkgA 导入的 S4 泛型上定义和导出隐式向用户公开了 S4 泛型,因此即使在 PkgB 的说明文件中指定 Imports: PkgA 也可以使用泛型。

One possible solution is to forcefully export forecast.midas_r.一种可能的解决方案是强制导出 forecast.midas_r。 This means manually updating NAMESPACE with export(forecast.midas_r) everytime after check("yourpackagename") .这意味着每次在check("yourpackagename")之后使用export(forecast.midas_r)手动更新NAMESPACE

What this does is make forecast.midas_r visible for package forecast .这样做是让forecast.midas_r对包forecast可见。 If forecast.midas_r is not exported, and exists only in the namespace of the package, then when package forecast is loaded, the generic forecast is overwritten with the identical function from package forecast .如果forecast.midas_r没有被导出,并且只存在于包的命名空间中,那么当包forecast被加载时,通用的forecast会被包forecast中的相同函数覆盖。 So when forecast is invoked on unknown object, R looks up for corresponding methods in package forecast and in general workspace.因此,当在未知对象上调用forecast时,R 会在包预测和一般工作区中查找相应的方法。 Since forecast.midas_r is a private method R does not find it and produces an error.由于forecast.midas_r是私有方法,R 没有找到它并产生错误。

This is not a perfect solution, since you need to manually update NAMESPACE , but it is a solution nevertheless.这不是一个完美的解决方案,因为您需要手动更新NAMESPACE ,但它仍然是一个解决方案。

This github issue was very helpful for me.这个github问题对我很有帮助。

Following the patterns of {sf}:遵循 {sf} 的模式:

  1. Don't export your functions in your namespace不要在命名空间中导出函数
  2. Copy the register_s3_method() function definition in a new R script or the same that has your methodsregister_s3_method() 函数定义复制到新的 R 脚本或具有您的方法的相同
  3. copy the scaffolding for registering all methods and repeat the body for all of the methods you may have in your package复制脚手架以注册所有方法,并为您的包中可能拥有的所有方法重复正文
register_all_s3_methods = function() {
  register_s3_method("pkg", "function", "class")
}
  1. In a new R script (preferably zzz.R ), have an .onLoad() hook containing在一个新的 R 脚本(最好zzz.R )中,有一个.onLoad()钩子包含
.onLoad = function(libname, pkgname) {
  register_all_s3_methods()
}
  1. Redocument your package重新记录您的包裹
  2. devtools::load_all()开发工具::load_all()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM