Code Fix

上級

S3の print メソッドで NextMethod() を省略すると基底クラスの表示が失われる

S3クラスに独自のprint.myclassを定義するとき、NextMethod()を呼ばずに独自の出力だけを書くと、継承元のクラスが本来表示していた情報が完全に失われます。

なぜエラーが出ないのか

出力: Name: Alice
(エラーなし)
Rは何も報告しません。文法としても実行としても正しいコードだからです
Name: Alice
実際の出力 — 期待した結果と食い違っている箇所

このエラーが出る典型パターン

パターン1

 1  x <- structure(list(name = "Alice", balance = 100), class = "account")
 2  print.account <- function(x, ...) {
 3    cat("Name:", x$name, "\n")
 4                
            ^
 5  }
 6  print(x)
出力: Name: Alice

print.accountがNextMethod()を呼ばないと、リストとしての基底表示(balanceを含む中身)が一切出ないまま終わります。

直し方: (空) を NextMethod() にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  x <- structure(list(title = "Sunrise", price = 1200), class = "ticket")
 2  print.ticket <- function(x, ...) {
 3    cat("Title:", x$title, "\n")
 4                
            ^
 5  }
 6  print(x)
出力: Title: Sunrise

NextMethod()を省略すると、priceを含む残りの情報がどこにも表示されなくなります。

直し方: (空) を NextMethod() にします。

この問題を解いてみる →

パターン3

 1  x <- structure(list(model = "Phone", battery = 80), class = "device")
 2  print.device <- function(x, ...) {
 3    cat("Model:", x$model, "\n")
 4                
            ^
 5  }
 6  print(x)
出力: Model: Phone

独自のprintメソッドは継承元の表示を自動で足してはくれず、NextMethod()を書かない限りbatteryの値は失われます。

直し方: (空) を NextMethod() にします。

この問題を解いてみる →

パターン4

 1  x <- structure(list(handle = "neo", level = 5), class = "profile")
 2  print.profile <- function(x, ...) {
 3    cat("Handle:", x$handle, "\n")
 4                
            ^
 5  }
 6  print(x)
出力: Handle: neo

levelの値を表示に含めたい場合は、独自の出力の後にNextMethod()を呼んで基底クラスの表示に処理を引き継ぐ必要があります。

直し方: (空) を NextMethod() にします。

この問題を解いてみる →

パターン5

 1  x <- structure(list(client = "Acme", amount = 300), class = "invoice")
 2  print.invoice <- function(x, ...) {
 3    cat("Client:", x$client, "\n")
 4                
            ^
 5  }
 6  print(x)
出力: Client: Acme

NextMethod()を呼ばない限り、amountを含む基底クラスの表示は一度も実行されません。

直し方: (空) を NextMethod() にします。

この問題を解いてみる →

よくある誤解

「独自のprintメソッドを定義しても、継承元の表示は自動で足されるはず」という思い込みは誤りです。S3のメソッドディスパッチは一度マッチしたメソッドだけを実行して終わるため、継承元の処理も実行したい場合はNextMethod()を明示的に呼ぶ必要があります。

実務での勘所

S3のメソッドディスパッチは、実際には単純な名前の組み立てで実現されています。print(x)を呼ぶと、Rはprintとxのクラス名(class(x)の1番目)を.でつないだprint.myclassという名前の関数を探しに行くだけです。オブジェクトのclass属性は複数のクラス名を持つベクトルになれる(c("myclass", "data.frame")のように)ため、NextMethod()はこのベクトルの次の要素に対応するメソッドを探して呼び出します。NextMethod()を呼ばなければ、この「次のクラスの処理に委ねる」という連鎖がそこで途切れてしまいます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)