You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用eval的情况下让checkargs保留qassert的变量名报错信息

问题

我希望封装checkmate库的qassert函数,实现一次性检查多个变量的规格,且断言错误时需显示不符合要求的变量名。我编写了checkargs函数循环处理输入参数,但使用get(var, envir=parent.frame())传递变量给qassert时,错误信息显示的是get(var, envir=parent.frame())这个代码字符串而非具体变量名。

qassert通过vname函数,利用deparse(eval.parent(substitute(substitute(x))))获取错误信息中的变量名。我需要找到一种方式包装变量传递逻辑,让R在deparse时得到具体变量名(如x),同时避免使用存在安全风险的eval(parse())方法。

现有示例代码:

checkargs <- function(...) {
   args<-list(...)
   for(var in names(args)) 
      checkmate::qassert(get(var,envir=parent.frame()),args[[var]])
      # 存在安全风险的字符串拼接替代方案
      #eval(parse(text=paste0("qassert(",var,",'",args[[var]], "')")),parent.frame())
} 

test_checkargs <- function(x, y) {checkargs(x='b',y='n'); print(y)}

# 正常执行场景
test_checkargs(T, 1)
# [1] 1

# 错误信息不符合预期
test_checkargs(1, 1)
# Error in checkargs(x = "b", y = "n") : 
#   Assertion on 'get(var, envir = parent.frame())' failed. Must be of class 'logical', not 'double'.
#
# 期望错误信息:
#   Assertion on 'x' failed. ...

解决方案

核心思路是通过substitute构造qassert的调用表达式,保留变量的符号名,让qassert能正确捕获原始变量名。具体实现如下:

checkargs <- function(...) {
  args <- list(...)
  for (var in names(args)) {
    # 动态构造qassert调用表达式,将变量名转为符号
    call_expr <- substitute(
      checkmate::qassert(var_sym, rule),
      list(var_sym = as.symbol(var), rule = args[[var]])
    )
    # 在父环境中执行构造好的表达式
    eval.parent(call_expr)
  }
}

原理说明

  • 使用as.symbol(var)将字符串形式的变量名转换为R的符号对象,这样substitute构造的表达式中,变量部分是原始的符号(如x)而非get(...)的调用。
  • eval.parent(call_expr)在checkargs的父环境(即调用checkargs的函数环境,比如test_checkargs)中执行表达式,确保qassert能访问到目标变量,同时内部的vname函数可以正确解析出变量名。
  • 这种方式避免了eval(parse())的安全风险,因为我们直接构造抽象语法树(AST)而非解析字符串。

验证测试

运行原测试函数:

test_checkargs <- function(x, y) {checkargs(x='b',y='n'); print(y)}

# 正常执行
test_checkargs(T, 1)
# [1] 1

# 错误场景输出预期信息
test_checkargs(1, 1)
# Error in checkargs(x = "b", y = "n") : 
#   Assertion on 'x' failed. Must be of class 'logical', not 'double'.

内容的提问来源于stack exchange,提问作者Will

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:24:19