【问题标题】:Documenting R6 classes and methods within R package in RStudio在 RStudio 的 R 包中记录 R6 类和方法
【发布时间】:2018-01-07 23:13:37
【问题描述】:

我正在为 R6 类及其方法的文档而苦苦挣扎。我的目标是在 RStudio 中自动完成这些方法。目前,我只获得了方法的名称,但没有获得通常使用roxygen2 记录带有参数的函数等的帮助信息。

目前,这是我的课:

#' @importFrom R6 R6Class
MQParameters <- R6::R6Class(
  'MQParameters',
  public=list(
    initialize=function(file_path=NA) {
      private$location <- file_path
      mq_parameters <- read.delim(file_path, stringsAsFactors=FALSE)
      mq_parameters <-
        setNames(mq_parameters$Value, mq_parameters$Parameter)
      private$mq_version <- unname(mq_parameters['Version'])
      private$fasta_file <-
        gsub('\\\\', '/', strsplit(mq_parameters['Fasta file'], ';')[[1]])
    },
    # this method returns the version
    getVersion=function() {
      private$mq_version
    },
    # this methods returns the fastafile.
    # @param new_param it is possible to rewrite the basedir.
    getFastaFile=function(new_basedir=NA) {
      if(is.na(new_basedir)) {
        private$fasta_file
      } else {
        file.path(new_basedir, basename(private$fasta_file))
      }
    }
  ),
  private=list(
    location=NULL,
    mq_version=NULL,
    fasta_file=NULL
  )
)

如果你有兴趣测试这个类,这里​​有一个可重现的小例子:

df <- data.frame(Parameter=c('Version', 'Fasta file'),
                 Value=c('1.5.2.8','c:\\a\\b.fasta'))
write.table(df, 'jnk.txt', sep='\t', row.names=F)

p <- MQParameters$new('jnk.txt')
p$getVersion()
# [1] "1.5.2.8"
p$getFastaFile()
# [1] "c:/a/b.fasta"
p$getFastaFile(new_basedir='.')
# [1] "./b.fasta"

我不知道如何记录参数,因为参数实际上属于创建者而不是类。函数中其他方法的参数呢?
用它的方法记录一个类的首选方法是什么?

我很想从 RStudio 获得“正常”功能,比如点击 F1 直接进入帮助页面。

通过搜索互联网,我已经在 Github 上看到了一些关于这个话题的报道,但它们已经有一年多的历史了。

更新

感谢mikeck 的回答,我现在有一个很好的关于该类及其方法的文档。但我仍然缺乏的是获得函数/方法的提示及其参数的可能性,就像这个屏幕截图中的一个通用函数:

我想知道我是否可以以某种方式手动注册我的函数,但由于它没有特定名称(它总是与您用于对象 OBJECTNAME$methodeCall() 的变量 objectname 耦合)我不知道如何这样做。

【问题讨论】:

    标签: r rstudio roxygen2 r6


    【解决方案1】:

    我的理解是,使用与您的班级相同的 @name 来记录 NULL 对象是最容易的,因为这提供了最大的灵活性。我在我的一个包中使用了 R6 类;您可以查看 roxygen here。我在下面包含了一个小示例:

    #' Python Environment
    #' 
    #' The Python Environment Class. Provides an interface to a Python process.
    #' 
    #' 
    #' @section Usage:
    #' \preformatted{py = PythonEnv$new(port, path)
    #'
    #' py$start()
    #' 
    #' py$running
    #' 
    #' py$exec(..., file = NULL)
    #' py$stop(force = FALSE)
    #' 
    #' }
    #'
    #' @section Arguments:
    #' \code{port} The port to use for communication with Python.
    #' 
    #' \code{path} The path to the Python executable.
    #' 
    #' \code{...} Commands to run or named variables to set in the Python process.
    #'
    #' \code{file} File containing Python code to execute.
    #' 
    #' \code{force} If \code{TRUE}, force the Python process to terminate
    #'   using a sytem call.
    #' 
    #' @section Methods:
    #' \code{$new()} Initialize a Python interface. The Python process is not 
    #'   started automatically.
    #'   
    #' \code{$start()} Start the Python process. The Python process runs 
    #'   asynchronously.
    #'
    #' \code{$running} Check if the Python process is running.
    #'   
    #' \code{$exec()} Execute the specified Python 
    #'   commands and invisibly return printed Python output (if any).
    #'   Alternatively, the \code{file} argument can be used to specify
    #'   a file containing Python code. Note that there will be no return 
    #'   value unless an explicit Python \code{print} statement is executed.
    #' 
    #' \code{$stop()} Stop the Python process by sending a request to the 
    #'   Python process. If \code{force = TRUE}, the process will be 
    #'   terminated using a system call instead.
    #'
    #' @name PythonEnv
    #' @examples
    #' pypath = Sys.which('python')
    #' if(nchar(pypath) > 0) { 
    #'   py = PythonEnv$new(path = pypath, port = 6011)
    #'   py$start()
    #'   py$running
    #'   py$stop(force = TRUE)
    #' } else 
    #' message("No Python distribution found!")
    NULL
    
    #' @export
    PythonEnv = R6::R6Class("PythonEnv", cloneable = FALSE,
      # actual class definition...
    

    还有其他替代(但类似)实现; this example 使用 @docType class 可能更适合您:

    #' Class providing object with methods for communication with lightning-viz server
    #'
    #' @docType class
    #' @importFrom R6 R6Class
    #' @importFrom RCurl postForm
    #' @importFrom RJSONIO fromJSON toJSON
    #' @importFrom httr POST
    #' @export
    #' @keywords data
    #' @return Object of \code{\link{R6Class}} with methods for communication with lightning-viz server.
    #' @format \code{\link{R6Class}} object.
    #' @examples
    #' Lightning$new("http://localhost:3000/")
    #' Lightning$new("http://your-lightning.herokuapp.com/")
    #' @field serveraddress Stores address of your lightning server.
    #' @field sessionid Stores id of your current session on the server.
    #' @field url Stores url of the last visualization created by this object.
    #' @field autoopen Checks if the server is automatically opening the visualizations.
    #' @field notebook Checks if the server is in the jupyter notebook mode.
    #' #' @section Methods:
    #' \describe{
    #'   \item{Documentation}{For full documentation of each method go to https://github.com/lightning-viz/lightining-r/}
    #'   \item{\code{new(serveraddress)}}{This method is used to create object of this class with \code{serveraddress} as address of the server object is connecting to.}
    #'
    #'   \item{\code{sethost(serveraddress)}}{This method changes server that you are contacting with to \code{serveraddress}.}
    #'   \item{\code{createsession(sessionname = "")}}{This method creates new session on the server with optionally given name in \code{sessionname}.}
    #'   \item{\code{usesession(sessionid)}}{This method changes currently used session on the server to the one with id given in \code{sessionid} parameter.}
    #'   \item{\code{openviz(vizid = NA)}}{This method by default opens most recently created by this object visualization. If \code{vizid} parameter is given, it opens a visualization with given id instead.}
    #'   \item{\code{enableautoopening()}}{This method enables auto opening of every visualisation that you create since that moment. Disabled by default.}
    #'   \item{\code{disableautoopening()}}{This method disables auto opening of every visualisation that you create since that moment. Disabled by default.}
    #'   \item{\code{line(series, index = NA, color = NA, label = NA, size = NA, xaxis = NA, yaxis = NA, logScaleX = "false", logScaleY = "false")}}{This method creates a line visualization for vector/matrix with each row representing a line, given in \code{series}.}
    #'   \item{\code{scatter(x, y, color = NA, label = NA, size = NA, alpha = NA, xaxis = NA, yaxis = NA)}}{This method creates a scatterplot for points with coordinates given in vectors \code{x, y}.}
    #'   \item{\code{linestacked(series, color = NA, label = NA, size = NA)}}{This method creates a plot of multiple lines given in matrix \code{series}, with an ability to hide and show every one of them.}
    #'   \item{\code{force(matrix, color = NA, label = NA, size = NA)}}{This method creates a force plot for matrix given in \code{matrix}.}
    #'   \item{\code{graph(x, y, matrix, color = NA, label = NA, size = NA)}}{This method creates a graph of points with coordinates given in \code{x, y} vectors, with connection given in \code{matrix} connectivity matrix.}
    #'   \item{\code{map(regions, weights, colormap)}}{This method creates a world (or USA) map, marking regions given as a vector of abbreviations (3-char for countries, 2-char for states) in \code{regions} with weights given in \code{weights} vector and with \code{colormap} color (string from colorbrewer).}
    #'   \item{\code{graphbundled(x, y, matrix, color = NA, label = NA, size = NA)}}{This method creates a bundled graph of points with coordinates given in \code{x, y} vectors, with connection given in \code{matrix} connectivity matrix. Lines on this graph are stacked a bit more than in the \code{graph} function.}
    #'   \item{\code{matrix(matrix, colormap)}}{This method creates a visualization of matrix given in \code{matrix} parameter, with its contents used as weights for the colormap given in \code{colormap} (string from colorbrewer).}
    #'   \item{\code{adjacency(matrix, label = NA)}}{This method creates a visualization for adjacency matrix given in \code{matrix} parameter.}
    #'   \item{\code{scatterline(x, y, t, color = NA, label = NA, size = NA)}}{This method creates a scatterplot for coordinates in vectors \code{x, y} and assignes a line plot to every point on that plot. Each line is given as a row in \code{t} matrix.}
    #'   \item{\code{scatter3(x, y, z, color = NA, label = NA, size = NA, alpha = NA)}}{This method creates a 3D scatterplot for coordinates given in vectors \code{x, y, z}.}
    #'   \item{\code{image(imgpath)}}{This method uploads image from file \code{imgpath} to the server and creates a visualisation of it.}
    #'   \item{\code{gallery(imgpathvector)}}{This method uploads images from vector of file paths \code{imgpathvector} to the server and creates a gallery of these images.}}
    
    
    Lightning <- R6Class("Lightning",
    ...
    )
    

    编辑

    如果您正在寻找一种在尝试使用类方法时显示 RStudio 工具提示的方法...不幸的是,我认为您不会找到不需要以某种方式对类进行编码的解决方案这消除了 R6 类的便利性和功能性。

    @f-privé 提供的答案可以满足您的需求——只需将该逻辑扩展到所有方法即可。例如,myclass$my_method 改为由

    访问
    my_method = function(r6obj) {
      r6obj$my_method()
    }
    obj$my_method()
    my_method(obj)      # equivalent
    

    换句话说,您需要为每个方法创建一个包装器。这显然不如仅使用obj$my_method() 方便编程,并且可能首先扼杀了使用 R6 类的用处。

    这里的问题实际上是 RStudio。 IDE 没有通过分析代码来识别 R6 类的好方法,并且无法区分已定义类的方法和列表或环境的元素。此外,RStudio 无法为任意函数提供帮助,例如:

    na.omit()         # tooltip shows up when cursor is within the parentheses
    foo = na.omit
    foo()             # no tooltip
    

    这非常类似于调用特定 R6 对象的方法。

    【讨论】:

    • 谢谢,这已经为我提供了一个很好的帮助文档!我仍在寻找的是获得 rStudio 的帮助。我想要的是自动完成和显示函数调用及其参数的黄色小窗口。现在我完成了函数名称,但没有关于参数的提示。我必须通过 ?MQParameters 搜索才能获得帮助,并且必须通读方法部分。
    • @drmariod 我编辑了我的答案以讨论工具提示问题,但不幸的是,我认为您不会在当前版本的 RStudio 中找到令人满意的解决方案。
    • @mikeck 我不记得我是如何采用这种方法的,但您可以查看 text2vec - github.com/dselivanov/text2vec/blob/master/R/model_LSA.R 中的示例。我只记得我在寻找记录 R6 的最佳方法方面也遇到了问题。
    • @mikeck 我希望rStudio 社区的某个人能对这如何可能给出一点提示,但我想我必须直接写信给他们。
    • 不幸的是,现在使用@docType class 似乎会导致错误Error: $ operator is invalid for atomic vectors。看起来它可能已经萎缩了。
    【解决方案2】:

    我认为 R 人不想使用$new(...) 来获取新类的实例。他们更喜欢有一个与类同名的函数来构造它的实例。

    所以,你可以做的是重命名你的 R6ClassGenerator MQParameters_R6Class 并创建另一个函数

    MQParameters <- function(file_path = NA) {
      MQParameters_R6Class$new(file_path)
    }
    

    然后,将此函数作为任何其他函数记录下来,您将从 RStudio 获得“显示函数调用及其参数的黄色小窗口”。以及快乐的 R 用户。

    【讨论】:

    • 我喜欢这个主意,但我仍然不满意!我认为这是更好的编程方式! :-) 让我们看看是否有更好的建议,但谢谢我可能会考虑。
    猜你喜欢
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多