【问题标题】:How to define an S4 prototype for inherited slots如何为继承的插槽定义 S4 原型
【发布时间】:2013-08-12 21:46:56
【问题描述】:

我有一个基类(我们称之为“A”),它的表示对于许多其他类来说是通用的。

因此我定义了其他类,比如“B”,来包含这个类。

我想将这些其他类 (B) 的原型设置为包含从 A 继承的插槽的默认值。我认为这很自然:

setClass("A", representation(a="character"))
setClass("B", contains="A", prototype(a = "hello"))

但它会产生错误:

Error in representation[!slots] : object of type 'S4' is not subsettable

不知道为什么会这样。如果我省略原型,我可以这样做:

setClass("B", contains="A")

然后破解我自己的生成器函数:

new_B <- function(...){ 
           obj <- new("B", ...)
           obj@a = "hello"
           obj
         }

然后使用 new_B() 基于原型创建我的对象,但与使用通用生成器 new("B") 并拥有我的原型相比,这非常粗糙和丑陋......

【问题讨论】:

    标签: r oop s4


    【解决方案1】:

    补充我的评论,而不是为问题提供新的答案,这是一个我们仍然按位置匹配参数的解决方案(因为我们为 B 类指定了额外的表示):

    .A <- setClass("A", representation(a="character"))
    .B <- setClass("B", representation(b="numeric"),
         prototype(a="hello"),
         contains="A")
    

    .A().B() 替换对 new("A")new("B") 的调用。在某种程度上,这是语法糖,但可以使对象构造更加透明

    ## construct an object using B's prototype, like new("B", b=1:3)
    > .B(b=1:3)
    An object of class "B"
    Slot "b":
    [1] 1 2 3
    
    Slot "a":
    [1] "hello"
    
    ## construct an object using A's prototype, like new("B", new("A"), b=1:3)
    > .B(.A(), b=1:3)
    An object of class "B"
    Slot "b":
    [1] 1 2 3
    
    Slot "a":
    character(0)
    

    (第二个示例使用 newB 的未命名参数来初始化继承类的事实。

    用户必须直接使用.A.B 不是很友好,例如,因为签名只是...,因此将记录为“查看A 类插槽的定义” '。这破坏了作为 OOP 优势的接口和实现的分离。此外,最后一个代码块(.B(.A(a=a), b=b).B(a=a, b=b))中的一种或另一种行为可能不是本意。所以改为提供一个 向用户公开的函数,也许做一些初始数据按摩

    A <- function(a=character(), ...) {
        ## nothing special, just a public constructor
        .A(a=a, ...)
    }
    
    B <- function(b, a="hello", ...) {
        a <- tolower(a)  ## no SHOUTing!
        .B(A(a=a), b=b)  ## A's public interface; no need for B to know A's details
    }
    

    函数 A 和 B 定义接口,在不将构造函数与类定义绑定的情况下向用户提供有关可接受参数的提示,并执行初步数据按摩。后者可以使 initialize 方法变得不必要,这是一件好事,因为它们有一个 complicated contract (它们应该初始化 并且 是复制构造函数,正如我们在上面看到的未命名参数应该初始化基类)大多数人都会出错。

    这些只是我的意见。

    【讨论】:

      【解决方案2】:

      你只需要命名参数:

      setClass("A", representation(a="character"))
      setClass("B", contains="A", prototype=prototype(a="hello"))
      

      【讨论】:

      • 另外,不是问题的一部分,.A &lt;- setClass("A", representation(a="character")) 允许 .A().A(a="foo") 作为构造函数,而不是 new("A")。我使用.A &lt;- ... 而不是A &lt;- ...,因为我仍然认为人们经常会编写一个带有命名参数等的更正式的构造函数,调用.A() 作为其最后一行。
      • @MartinMorgan 谢谢,好技巧!查看使用.A() 的更正式的构造函数的示例可能会很有用,不知道为什么您更喜欢new("A")?。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多