尽管存在默认方法,但S3通用函数中没有适用的方法。

3
这很棘手,因为这个问题只在包的上下文中发生 - 当在全局命名空间中定义时,一切都按预期工作。
我定义了一个名为coerce_na_range()的S3通用函数,它有两种方法:coerce_na_range.factor()coerce_na_range.default()coerce_na_range()被导出,但两种方法没有被导出。(该函数的目的是在给定范围内将编码为字符或因子标签的数字替换为NA。)
当在全局命名空间中定义时,如果我将字符向量传递给coerce_na_range(),它会将其分派到coerce_na_range.default()并按预期工作:
vec <- c("green", "yellow", "-9", "red", "-1")
coerce_na_range(vec)
# [1] "green"  "yellow" NA       "red"    NA   

然而,如果我在新的会话中加载软件包,它似乎会忽略默认方法:
library(lighthouse)

vec <- c("green", "yellow", "-9", "red", "-1")
coerce_na_range(vec)
# Error in UseMethod("coerce_na_range") : 
#  no applicable method for 'coerce_na_range' applied to an object of class "character"

我认为问题不在于方法没有被导出?例如,tidyr:::full_seq.Date()等未被导出,而tidyr::full_seq()显然是有效的。
该软件包托管在https://github.com/ccsarapas/lighthousecoerce_na_range()的代码、其方法和一些依赖它们的函数如下:
#' Suppress NA warning when coercing to numeric
#'
#' Coerces `x` to numeric. If `x` cannot be coerced, returns `NA` and suppresses
#' coercion warning.
#'
#' @export
try_numeric <- function(x) {
  if (is.factor(x)) {
    warning(
      "`x` is a factor and will be converted based on factor codes, not factor labels."
    )
  }
  withCallingHandlers(
    warning = function(w) {
      if (conditionMessage(w) == "NAs introduced by coercion") {
        rlang::cnd_muffle(w)
      }
    },
    as.numeric(x)
  )
}

#' @rdname try_numeric
#'
#' @export
try.numeric <- function(x) try_numeric(x)

#' Generate NA values of appropriate type
#'
#' Returns compatible `NA` based on `x`. This is usually of the same type as `x`
#' (e.g., `NA_real_` if `x` is a double vector). If `x` is a factor, will
#' return `NA_character_` if `factor_as_character = TRUE` (the default) and
#' `NA_integer_` otherwise.
#'
#' @export
na_like <- function(x, factor_as_character = TRUE, match_length = FALSE) {
  stopifnot("`x` must be an atomic vector" = is.atomic(x))
  type_out <- if (factor_as_character && is.factor(x)) "character" else typeof(x)
  length_out <- if (match_length) length(x) else 1L
  rep(methods::as(NA, type_out), length_out)
}


#' Set NA values based on numbers stored as strings.
#'
#' Changes values coercible to numeric in range `range_min`:`range_max` to `NA`.
#' Useful for imported SPSS files.
#'
#' @export
coerce_na_range <- function(x, ...) UseMethod("coerce_na_range")
coerce_na_range.default <- function(x, range_min = -Inf, range_max = -1) {
  coerced <- try.numeric(x)
  dplyr::if_else(
    is.na(coerced) | (coerced < range_min) | (coerced > range_max),
    x,
    na_like(x)
  )
}
coerce_na_range.factor <- function(x, range_min = -Inf, range_max = -1) {
  lvls <- levels(x)
  coerced <- try.numeric(as.character(lvls))
  lvls <- lvls[is.na(coerced) | coerced < range_min | coerced > range_max]
  factor(x, levels = lvls)
}

1
我认为如果你在 coerce_na_range.factor 函数上面放置一个 #' @export 行,那么 Roxygen 将把它识别为 S3 方法,并将 S3method(coerce_na_range, factor) 添加到你的包 NAMESPACE 文件中。默认方法也是如此。 - undefined
4
对于 roxygen,你仍需要在函数上使用 #' @export。它足够智能,可以识别它们是 S3 方法,并不会使用完整名称导出它们,但会设置通用方法,以便包的使用者可以使用它们。参见 https://dev59.com/6mw15IYBdhLWcg3wmMx9#22598245。 - undefined
1
@StéphaneLaurent 你确定这是当前的建议吗?帮助页面说,只有在方法是来自建议包的通用函数时才需要使用该函数。我非常确定,在最新版本的roxygen中,你只需要使用@export,它就会做正确的事情。另请参见https://r-pkgs.org/Metadata.html#sec-metadata-namespace - undefined
1
@CarlWitthoft,我们可以看到OP正在使用Roxygen2,因此他们不应该手动调整NAMESPACE。 Roxygen提供了足够的抽象,使人们可以忘记NAMESPACE文件的细节。但是知道它如何/为什么工作总是好的。 - undefined
感谢@MrFlick和AllanCameron,非常感谢,那很有道理。如果你们中有人想要回复作为答案,我会接受的。 - undefined
显示剩余3条评论
1个回答

3
generic.factor 前面添加 @export 标签,并运行 roxgenize 应该可以解决这个问题。
# [...]
#' @exportS3Method coerce_na_range factor
coerce_na_range.factor <- function(x, range_min = -Inf, range_max = -1) {
  lvls <- levels(x)
  coerced <- try.numeric(as.character(lvls))
  lvls <- lvls[is.na(coerced) | coerced < range_min | coerced > range_max]
  factor(x, levels = lvls)
}

然后在控制台中运行: roxygen2::roxygenise()

1
虽然这样可以工作,但这不是正确的方法。例如使用@exportS3Method your_generic your_method - undefined
1
@Onyambu;这似乎与上面评论中的建议相矛盾。https://stackoverflow.com/questions/74521311/no-applicable-method-for-s3-generic-despite-existence-of-default-method#comment131548580_74521311 - undefined
1
@user20650 我试过了。看起来这两种方法是等效的。唯一的区别就是 export s3method 标签的明确性。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接