【发布时间】:2015-04-02 18:07:24
【问题描述】:
on.exit 在函数退出时调用代码,但我应该如何以及何时使用它?
【问题讨论】:
-
Python 的 with 语句的(粗略的)R 等效项,也称为上下文管理器。
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 包中可以找到另一种语法,其中defer 是on.exit 的便捷包装,scope_* 函数的工作方式与前面提到的包中的with_* 函数类似。
在这个例子中,sqlite_get_query 连接到一个 sqlite 数据库,确保
查询运行后连接总是关闭。 cookies
数据库要求您在计算机上安装了 firefox,并且您可以
需要调整路径才能找到cookies文件。
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 是一个使用 base 创建绘图的函数
图形。 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,覆盖全局 margin parameter,在绘图完成后使用 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_optionslater 等效:scope_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)【讨论】: