在R中使用eval()实现装饰器:父环境不一致问题求助
R中装饰器的环境继承问题
我试图在R中创建一个装饰器,让被装饰函数在其他函数内部调用时,能以调用它的函数的环境作为父环境。我认为可以通过eval()实现,但遇到了困难。
当前代码
library(rlang) ### 装饰器定义 dynamicCall <- function(decorated) { dynamicEnv <- new.env(parent = parent.frame()) attach(dynamicEnv) eval(decorated, enclos = dynamicEnv) print('dynamicCall中的父环境') print(env_parents(dynamicEnv)) } ### 待装饰函数 f <- function() { print('f中的父环境') print(env_parents(environment())) } ### 调用装饰器的函数 g <- function() { print('调用环境') print(environment()) dynamicCall(f()) } g()
运行输出
[1] "调用环境" <environment: 0x00000211a91ff840> [1] "f中的父环境" [[1]] <env: global> [1] "dynamicCall中的父环境" [[1]] <env: 0x00000211a91ff840> [[2]] <env: global>
问题分析
核心问题是调用dynamicCall(f())时,f()已经在g()的环境中提前执行了,传递给装饰器的是f的执行结果而非函数对象。此时eval()根本没起到控制执行环境的作用,f的执行环境还是它默认的封闭环境(全局环境),自然和dynamicEnv的父环境链不一致。
修正方案
要让装饰器控制被装饰函数的执行环境,需要传递函数对象而非执行结果,并在装饰器内部指定环境执行函数体:
library(rlang) ### 装饰器定义 dynamicCall <- function(decorated_func) { # 获取调用装饰器的环境(即g()的运行环境) caller_env <- parent.frame() # 创建以调用环境为父环境的动态环境 dynamicEnv <- new.env(parent = caller_env) # 在dynamicEnv环境中执行被装饰函数的函数体 eval(body(decorated_func), envir = dynamicEnv) cat("dynamicCall中的父环境:\n") print(env_parents(dynamicEnv)) } ### 待装饰函数 f <- function() { cat("f中的父环境:\n") print(env_parents(environment())) } ### 调用装饰器的函数 g <- function() { cat("调用环境:\n") print(environment()) # 传递函数对象f,而非执行f() dynamicCall(f) } g()
修正后输出
调用环境: <environment: 0x00000211a91ff840> f中的父环境: [[1]] <env: 0x00000211a9200120> [[2]] <env: 0x00000211a91ff840> [[3]] <env: global> dynamicCall中的父环境: [[1]] <env: 0x00000211a91ff840> [[2]] <env: global>
关键修正点
- 传递
f而非f():确保装饰器拿到的是函数对象,而非提前执行后的结果 - 使用
body(decorated_func)获取函数体,通过eval(..., envir = dynamicEnv)指定执行环境,让f的运行环境为dynamicEnv dynamicEnv的父环境设为g()的运行环境,因此f的环境父链会包含g()的环境,符合预期
内容的提问来源于stack exchange,提问作者kemoT
相关产品推荐
相关产品推荐

