我应该如何以及何时使用on.exit?

Ric*_*ton 45 r r-faq

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

Ric*_*ton 55

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

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


使用的包 on.exit

withr软件包包含许多with_*功能,可以更改设置,运行一些代码,然后更改设置.这些功能也出现在devtools包中.

later包中找到了一个替代语法,它defer是一个方便的包装器on.exit,scope_*函数的工作方式类似于with_*前面提到的包中的函数.


数据库连接

在此示例中,sqlite_get_query连接到sqlite数据库,确保在查询运行后始终关闭连接.该cookies 数据库需要你有你的机器上安装了Firefox,你可能需要调整的路径来寻找饼干文件.

library(RSQLite)
sqlite_get_query <- function(db, sql)
{
  conn <- dbConnect(RSQLite::SQLite(), db)
  on.exit(dbDisconnect(conn))
  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"
)
Run Code Online (Sandbox Code Playgroud)

文件连接

在此示例中,read_chars包装readChars,确保在完成读取后始终关闭与文件的连接.

read_chars <- function(file_name)
{
  conn <- file(file_name, "r")
  on.exit(close(conn))
  readChar(conn, file.info(file_name)$size)
}

tmp <- tempfile()
cat(letters, file = tmp, sep = "")
read_chars(tmp)
Run Code Online (Sandbox Code Playgroud)

临时文件

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

history_lines <- function()
{
  f <- tempfile()
  on.exit(unlink(f))
  savehistory(f)
  readLines(f, encoding = "UTF-8")
}
Run Code Online (Sandbox Code Playgroud)

保存基础图形

在此示例中,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())
  plot_fn()
}

save_base_plot(my_plot, "testcars.png")
Run Code Online (Sandbox Code Playgroud)

暂时设置基本图形选项

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

plot_with_big_margins <- function(...)
{
  old_pars <- par(mar = c(10, 9, 9, 7))  
  on.exit(par(old_pars))
  plot(...)
}

plot_with_big_margins(with(cars, speed, dist))
Run Code Online (Sandbox Code Playgroud)

withr/ devtools等价物:with_par


暂时设置全局选项

在这个例子中,create_data_frame是一个创建一个的函数data.frame. create_data_frame确保创建的对象不包含显式因子.

create_data_frame <- function(){
  op <- options(stringsAsFactors = FALSE)
  on.exit(options(op))

  data.frame(x=1:10)
}
Run Code Online (Sandbox Code Playgroud)

withr/ devtools等价物:等价物:with_options
laterscope_options


其他例子

  • 设置工作目录(withr::with_dir,later::scope_dir)
  • 设置区域设置组件(withr::with_locale)
  • 设置环境变量(withr::with_envvars,later::scope_env_var)
  • 设置库路径(withr::with_libpaths)
  • 使用接收器重定向输出
  • 暂时加载包(withr::with_package,withr::with_namespace)