如何在R6类中遍历指定的嵌套子类?
在R6类中遍历指定嵌套子类的解决方案
问题描述
刚接触R6类,正在熟悉其用法,当前在开发嵌套类时需要遍历指定的子类实例。示例中Person类的arm、leg、head均为BodyPart类的实例,同时通过bodyParts列表标记需要遍历的目标子类。但现有getVolume函数使用get(bodyPart)$volume仅能访问全局环境中的对象,无法访问Person实例自身绑定的子类属性,不知道如何在循环中通过self访问实例内部的子类。
核心需求:最终要支持自定义数量的子类(通过...参数),通过bodyParts列表筛选目标子类,实现函数与多个指定子类的交互。
示例代码:
BodyPart <- R6::R6Class(classname = "BodyPart", public = list( width = numeric(), volume = numeric(), initialize = function(name = NA, width = 3, volume = 2){ self$name <- name self$width <- width self$volume <- volume } ) ) Person <- R6::R6Class(classname = "Person", public = list( name = character(), age = numeric(), bodyParts = list(), arm = environment(), leg = environment(), head = environment(), initialize = function(name = NA, age = NA, bodyParts = list("arm", "leg", "head"), arm = NA, leg = NA, head = NA){ self$name <- name self$age <- age self$bodyParts <- bodyParts self$arm <- arm self$leg <- leg self$head <- head }, getVolume = function(){ for(bodyPart in self$bodyParts){ volume <- get(bodyPart)$volume print(paste(bodyPart, volume)) } } ) ) LongArm <- BodyPart$new() MediumLeg <- BodyPart$new() CubeHead <- BodyPart$new() Person <- Person$new(name = "Jake", age = 20, arm = LongArm, leg = MediumLeg, head = CubeHead) Person$getVolume()
解决方案
1. 修复实例属性访问问题
原代码的核心问题是使用get()从全局环境取对象,而正确的做法是通过self[[bodyPart]]从当前Person实例中按名称获取属性。修改getVolume函数如下:
getVolume = function(){ for(bodyPart in self$bodyParts){ volume <- self[[bodyPart]]$volume print(paste(bodyPart, volume)) } }
[[运算符支持通过字符串索引访问R6实例的属性,这样就能正确获取到Person实例内部绑定的BodyPart子类。
2. 支持自定义数量子类的优化方案
如果要实现动态添加任意数量的子类,无需单独定义arm、leg等属性,可以用统一列表存储所有子类,配合...参数实现灵活初始化:
优化后的完整代码:
BodyPart <- R6::R6Class(classname = "BodyPart", public = list( name = character(), width = numeric(), volume = numeric(), initialize = function(name = NA, width = 3, volume = 2){ self$name <- name self$width <- width self$volume <- volume } ) ) Person <- R6::R6Class(classname = "Person", public = list( name = character(), age = numeric(), bodyParts = list(), # 统一存储所有身体部位子类 body_parts_store = list(), initialize = function(name = NA, age = NA, bodyParts = NULL, ...){ self$name <- name self$age <- age # 将...参数转换为键值对列表,键为部位名称,值为BodyPart实例 self$body_parts_store <- list(...) # 若未指定bodyParts,默认遍历所有传入的子类 if(is.null(bodyParts)){ self$bodyParts <- names(self$body_parts_store) } else { self$bodyParts <- bodyParts } }, getVolume = function(){ for(bodyPart in self$bodyParts){ part <- self$body_parts_store[[bodyPart]] # 增加类型检查,避免无效输入 if(inherits(part, "BodyPart")){ print(paste(bodyPart, part$volume)) } else { warning(paste(bodyPart, "不是有效的BodyPart实例")) } } } ) ) # 使用示例 LongArm <- BodyPart$new(name = "arm", volume = 5) MediumLeg <- BodyPart$new(name = "leg", volume = 8) CubeHead <- BodyPart$new(name = "head", volume = 3) BigHand <- BodyPart$new(name = "hand", volume = 2) # 自定义新增部位 # 初始化Person,指定要遍历的子类 Jake <- Person$new(name = "Jake", age = 20, bodyParts = list("arm", "leg", "hand"), arm = LongArm, leg = MediumLeg, head = CubeHead, hand = BigHand) Jake$getVolume()
方案优势
- 通过
[[运算符解决了实例内部子类的访问问题 - 用统一列表存储子类,配合
...参数实现任意数量子类的动态扩展 - 增加类型检查,提升代码健壮性
- 支持默认遍历所有子类或自定义筛选,灵活性更高
内容的提问来源于stack exchange,提问作者Greacus
相关产品推荐
相关产品推荐

