Подтвердить что ты не робот

Как и когда следует использовать on.exit?

on.exit вызывает код при выходе из функции, но как и когда я должен его использовать?

4b9b3361

Ответ 1

Преимущество on.exit заключается в том, что он вызывается, когда функция выходит, независимо от того, была ли выбрана ошибка. Это означает, что его основное назначение - очистка после рискованного поведения. В этом контексте рискованный подход обычно означает доступ к ресурсам за пределами R (что, следовательно, не может быть гарантировано работать). Обычные примеры включают подключение к базам данных или файлу (когда соединение должно быть закрыто, когда вы закончили, даже если произошла ошибка) или сохранение графика в файл (где графическое устройство должно быть закрыто впоследствии).

Вы также можете использовать on.exit для поведения с низким уровнем риска с побочным эффектом, например, для установки рабочего каталога.


Пакеты, которые используют on.exit

Пакет withr содержит множество функций with_*, которые меняют настройку, запускают некоторый код, а затем изменяют настройку. Эти функции также отображаются в пакете devtools.

Альтернативный синтаксис находится в пакете later, где defer - это удобная обертка для on.exit и scope_* функции работают как функции with_* в ранее упомянутых пакетах.


Соединения с базой данных

В этом примере sqlite_get_query подключается к базе данных sqlite, обеспечивая что соединение всегда закрывается после выполнения запроса. cookies для базы данных требуется, чтобы на вашем компьютере был установлен firefox, и вы можете необходимо настроить путь для поиска файла cookie.

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"
)

Подключение файлов

В этом примере 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)

Временные файлы

В следующем примере, адаптированном из CodeDepends, используется временный файл для сохранения истории сеанса. Этот временный файл не нужен, как только функция вернется, чтобы она была удалена.

history_lines <- function()
{
  f <- tempfile()
  on.exit(unlink(f))
  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())
  plot_fn()
}

save_base_plot(my_plot, "testcars.png")

Временное определение параметров базовой графики

В этом примере plot_with_big_margins вызывает plot, переопределяя глобальный mar gin par ameter, используя on.exit до reset после завершения графика.

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))

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)
}

withr/devtools эквивалент: with_options
later эквивалент: scope_options


Другие примеры

  • Настройка рабочего каталога (withr::with_dir, later::scope_dir)
  • Установка языковых компонентов (withr::with_locale)
  • Настройка переменных среды (withr::with_envvars, later::scope_env_var)
  • Настройка путей библиотеки (withr::with_libpaths)
  • Перенаправление вывода с раковиной
  • Временная загрузка пакета