r语言 - 我应该如何以及何时使用 on.exit


on.exit函数

退出时调用代码,但是我应该如何以及何时使用它?

on.exit的优点

是,无论是否抛出错误,函数退出时都会被调用。 这意味着它的主要用途是在危险行为后进行清理。 在这种情况下,风险通常意味着访问 R 之外的资源(因此不能保证正常工作)。 常见示例包括连接到数据库或文件(完成后必须关闭连接,即使存在错误),或将打印保存到文件(之后必须关闭图形设备)。

您还可以将on.exit用于具有副作用的低风险行为,例如设置工作目录。

通常,您应该在 on.exit() 中设置add = TRUE。请参阅 https://adv-r.hadley.nz/functions.html?q=on.exit#on-exit。

<小时 />

使用on.exit的软件包

withr包包含许多with_*函数,这些函数可以更改设置、运行一些代码,然后更改回设置。 这些函数也出现在devtools包中。

later包中找到了另一种语法,其中deferon.exit的方便包装器,并且scope_*函数的工作方式与前面提到的包中的with_*函数类似。

<小时 />

数据库连接

在此示例中,sqlite_get_query连接到 sqlite 数据库,确保查询运行后,连接始终关闭。 The cookies数据库要求您在计算机上安装了Firefox,并且您可以需要调整路径才能找到 cookie 文件。

library(RSQLite)
sqlite_get_query <- function(db, sql)
{
  conn <- dbConnect(RSQLite::SQLite(), db)
  on.exit(dbDisconnect(conn), add = TRUE)
  dbGetQuery(conn, sql)
}
cookies <- dir(
  file.path(Sys.getenv("APPDATA"), "Mozilla", "Firefox"), 
  recursive  = TRUE, 
  pattern    = "cookies.sqlite$",
  full.names = TRUE
)[1]
sqlite_get_query(
  cookies, 
  "SELECT `baseDomain`, `name`, `value` FROM moz_cookies LIMIT 20"
)
<小时 />

文件连接

在此示例中,read_chars包装readChars ,确保连接到文件总是在读取完成后关闭。

read_chars <- function(file_name)
{
  conn <- file(file_name, "r")
  on.exit(close(conn), add = TRUE)
  readChar(conn, file.info(file_name)$size)
}
tmp <- tempfile()
cat(letters, file = tmp, sep = "")
read_chars(tmp)
<小时 />

临时文件

以下改编自 CodeDepends 的示例使用临时文件来保存会话历史记录。函数返回后不需要此临时文件,因此将其删除。

history_lines <- function()
{
  f <- tempfile()
  on.exit(unlink(f), add = TRUE)
  savehistory(f)
  readLines(f, encoding = "UTF-8")
}
<小时 />

保存基础图形

在此示例中,my_plot 是一个使用基数创建绘图的函数图形。 save_base_plot接受函数和文件以将其保存到,使用 on.exit确保图形设备始终处于关闭状态。

my_plot <- function()
{
  with(cars, plot(speed, dist))
}
save_base_plot <- function(plot_fn, file)
{
  png(file)
  on.exit(dev.off(), add = TRUE)
  plot_fn()
}
save_base_plot(my_plot, "testcars.png")
<小时 />

临时设置基础图形选项

在此示例中,plot_with_big_margins调用 plot ,覆盖全局mar杜松子酒par ameter,在绘图完成后使用 on.exit 重置它。

plot_with_big_margins <- function(...)
{
  old_pars <- par(mar = c(10, 9, 9, 7))  
  on.exit(par(old_pars), add = TRUE)
  plot(...)
}
plot_with_big_margins(with(cars, speed, dist))

withr/devtools等效:with_par

<小时 />

临时设置全局选项

在此示例中,create_data_frame 是创建 data.frame 的函数。 create_data_frame 确保创建的对象不包含显式因素。

create_data_frame <- function(){
  op <- options(stringsAsFactors = FALSE)
  on.exit(options(op), add = TRUE)
  
  data.frame(x=1:10)
}

withr/devtools等效:with_options
later等效:scope_options

<小时 />

其他例子

  • 设置工作目录 ( withr::with_dirlater::scope_dir
  • 设置
  • 区域设置组件 ( withr::with_locale
  • 设置环境变量 ( withr::with_envvarslater::scope_env_var
  • 设置库路径 ( withr::with_libpaths
  • 使用接收器重定向输出
  • 临时加载包 ( withr::with_packagewithr::with_namespace