チュートリアル / 押忍!Softimage マニアクス
第3回:もっと Softimage×Python Vol.1

2013.07.10

  • ゲーム
  • スクリプト・API
  • チュートリアル
  • 上級者
  • 中級者

はじめに

押忍!(私も例にもれず押忍で始めますw)
はじめまして。株式会社セガの岸本と申します。Softimageに関してコラムを書かせていただくことになりました。今後ともよろしくお願いします。

・コラムをPDFでダウンロード(319KB)

Softimage × Python

Pythonには魅力的な機能がたくさんあります。でも実際Softimage上でそのすばらしい機能を使ってみようと思ったとき、「あれ?これSoftimageではどうやって使えばいいんだろう?」と悩むことがしばしばありました。情報をなかなか見つけられずに苦労したのを覚えています。
そんなわけで、私のコラムではSoftimage × Pythonをうまく使うためのTipsを紹介していければと思います。

デコレータを使ってみよう!

第一回となる今回はVBScript、JScriptにない、Pythonならではということを意識して「デコレータ」を紹介したいと思います。
デコレータとはその名のとおり関数をデコレートするための機能です。
ここでいうデコレートってどういうことでしょう?

デコレート?

例えば
■ スクリプトの処理が重い!どこが重いか知りたいからそれぞれのコマンド実行時間を計測できるようにしたい!
■ コマンドを実行した後元に戻すのにアンドゥを連打しなきゃいけない!一回で戻るようにしたい!
■ SIのバージョンによってはうまく動作しないコマンドがある!特定のバージョンでは何もしないようにしたい!

などなど、「ちょっとだけコマンドに機能を追加したい!」と思ったことはありませんか?
この機能追加が「デコレート」にあたります。
これらは関数内部に目的のコードを書くことで実現できるものばかりですが素直に書いてしまうとちょっと困ることになります。

関数の実行時間の計測するスクリプトを書くとしたらこんな感じでしょうか?
Softimageのスクリプトエディタにペーストして実行してみてください。

SoftImagePython_001_sampleをダウンロード

import time # 時間に関する機能を持ったtimeモジュールを読み込む

# テスト関数の定義
def TestA():
starttime = time.time() # 処理実行前の時刻をとってくる
# この関数でやらせたい処理 ----------------------↓
LogMessage(u'TestA関数を実行しました!')
# --------------------------------↑
endtime = time.time()# 処理実行後の時刻をとってくる
LogMessage(endtime - starttime)# 実行後の時刻から実行前の時刻を引いたものを表示

# テスト関数の実行
TestA()

私の環境(Softiamge2013)ではヒストリペインにこのようなログが出ました。
SoftImage2013 ヒストリペイン ログ

テスト関数「TestA」の実行結果に続いて経過時間が表示されます。単位は秒ですので、0.002秒以下しかかかっていないことがわかります。「TestA関数を実行しました!」とログ表示するだけなので早いですね。

ところでこのコード、ちょっと見づらくないですか?
関数自体が行いたい処理「print u'TestA関数を実行しました!'」部分と経過時間を表示する部分がゴチャまぜです。それに実行時間をログするのにも処理時間がかかるのでログ表示したくないときがあるかもしれません。その場合最初と最後の2箇所をコメントアウトしないといけません。
なんとかもうちょっとすっきりしないものでしょうか?

デコレータはそんな要望にスマートに答えてくれます。
デコレータを使ったコードはこんな感じになります。
スクリプトエディタにペーストして実行するとさっきのスクリプトと同じような結果になると思います。

SoftImagePython_002_sampleをダウンロード

import time # 時間に関する機能を持った time モジュールを読み込む

# 時間経過のログを表示するデコレータの定義
def LogTime(func):

# 与えられた関数の代わりに実行する新しい関数の定義
def new_func(*args, **kwargs):
starttime = time.time() # 処理実行前の時刻をとっておく
rtn = func(*args, **kwargs) # 与えられた関数(ここではTestA)を実行して戻り値をとっておく

endtime = time.time() # 処理実行後の時刻をとっておく
LogMessage(endtime - starttime)# 実行後の時刻から実行前の時刻を引いたものを表示
return rtn# とっておいた戻り値を戻す

# 新しく作った関数を戻す
return new_func

# テスト関数の定義
@LogTime# @ + デコレータ名で直後に置かれた関数をデコレート
def TestA():
LogMessage(u'TestA 関数を実行しました!')

# テスト関数の実行
TestA()

どうでしょうか?コードが長くなって複雑になったように思われるかもしません。
でもTestA関数の部分を見てください。余計なコードがなくなってすっきりしたと思いませんか?これなら関数で本当に実行したい処理に集中してコードが書けそうです。
良く見ると今までなかった「@***」という行が追加されています。これがデコレータの呼び出し合図です。この「@***」を関数の前につけると、その関数は「***」デコレータによってデコレートされることになります。どのようにデコレートするかは上のLogTime関数に書かれています。

わかりにくいデコレータ

見ていただいたようにデコレータを使えば1行追加するだけで簡単に機能追加できてとても便利です。
ただこのデコレータ、便利なんだけどちょっとわかり辛い。それは見えないところでデコレータ用の特別な処理が行われるからです。
少しでもわかりやすくするためにどんな処理が行われているのかマンガにしてみました。

スクリプト読み込み時 漫画

よけいわかり辛いでしょうかw???
厳密にはツッコミどころ満載だと思いますがきっと裏でパイソンさんはこんな感じのことをしてるんじゃないかと思います。
デコレータは対象となる関数が作られるタイミングでその関数を受け取って実行され、Pythonはデコレータから戻ってきたものを元の関数と置き換えます。
今回の例では対象となる関数(TestA)が引数funcとしてデコレータに渡され、内部でそのfuncを実行して経過時間を表示するようにつくられたnew_func関数を作成し戻しています。元の関数(TestA)は見えないところでこの新しい関数に置き換えられているのでTestAを実行しようとしたとき実際には新しい関数(new_func)が実行されて経過時間が表示されるという訳です。
おおざっぱに言うとデコレータは「関数を置き換える特別な関数」、ということができると思います。

まとめ

なんとなくデコレータの便利さと、どういうものなのかわかっていただけたでしょうか?
デコレータには以下のようなメリットがあると思います。
● 対象関数の内容とは完全に切り離すことができる
● 開始と終了に処理を追加しなければならない場合も1行で済む
関数の内容と直接関係ないようなコードをたくさん書いているような場合にはどんどんデコレートしちゃいましょう。

ここまで来て「あれ?」と思った方もいらっしゃるかもしれません。このままだと、「Softimageではどう使うのか?」という冒頭でお話した部分がすっぽり抜けてしまっています。
今回のコードはスクリプトエディタで実行しないと動きません。せっかく作ったデコレータはほかのいろんなスクリプトから呼び出して使いたいですよね?デコレータはSoftimageのコマンドに対しても使えますが、デコレータ用につくった関数はコマンドにできません。コマンド化すると他からの呼び出しはできますがちゃんとデコレータとしては動いてくれなくなってしまいます。
デコレータをどこからでも呼べるようにするにはPython自体にカスタムモジュールとして登録する必要があります。そしてそれはPythonをPythonらしく使うために欠かせない部分だと言っても過言ではありません。

ということで次回は簡単にカスタムモジュールを登録する方法についてご紹介し、今回作ったデコレータをどこからでも呼べる状態にしたいと思います。

著者

岸本 智

岸本 智

株式会社セガ 第一研究開発本部 デザイン開発セクション テクニカルアーティスト

製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。