2011-12-05

関数と引数 その1(python入門)

前回、「ToolSets と python の組み合わせ その1」の続きを書こうといきまいたのですが、どうやっても関数と引数の説明を避けて通れないので、今回は関数と引数の説明を。

自分でも、シッカリとプログラムを勉強したわけでもないですし、授業等で習ったわけでもないので、全て独学です。なので上手く説明できるか・・・

ともかく関数というのは 、何かを実行するための「機能」です。たとえば今回まずやりたいのは、ファイルのコピーとします。ファイルのコピーには最低でも

「どこにある何て名前のファイルを(コピー元ファイルパス)」を
「どこ(コピー先ファイルパス)」へコピーするのか。

この二つのことを、この「機能」を伝えてやる必要があります。
このある「機能」にたいして「最低限伝えなければならないこと」を引数といいます。「機能」そのものが関数になります。

関数は

def hoge(引数):
    実行文   (※hogeは適当にかいてます。)

という形で定義されます。
「def」 に続けて 関数名+()、でその後に:(コロン)です。
実行文はコロンの後に改行してtab(インデント)や複数の半角スペースで段差をつけます。この段差でこの関数がココからココまでと宣言しているので段差は重要です。

実際に書いてみます。
-------------------------------------------------------
def hoge():
    print "Hello!"
-------------------------------------------------------
実行する場合は、hoge() で実行します。
print はスクリプトエディタのアウトプット側に指定されたことを表示するってことです。
なので、この関数は実行すると Hello! と表示するスクリプトということになります。
また表示したいことは Hello! そのもので文字列(文字そのもの)なので、" " で表示したい文字列を囲っています。


上のアウトプット側には Hello! が出ていますが、" " は出ていません。
たとえば、

-------------------------------------------------------
A = "Hello!"
def hoge():
    print A
-------------------------------------------------------

として、hoge() を実行すると、Aが表示されず A = "Hello!" で定義された、Hello! が表示されます。


ここでもやはり、" " がなく Hello! と表示されます。

さっきは、A = "Hello!" であらかじめ A を定義しましたが、関数の魅力は後から変数を指定できるということでしょうか。つまり、

何を表示するかは未定だけど、とりあえず、あとから決まった事を表示する

といったところです。
具体的には

-------------------------------------------------------
def hoge(A):
    print A
-------------------------------------------------------

とします。何をprintするかは決まってないけどとりあえず A としておいて、関数として hoge(A) で、とりあえずprintすることは A で仮に置いておくけど、あとからちゃんと決めてね という感じです。

で hoge() をすると


となってなにやらエラーが出ます。
とりあえず、A で仮置きしたけど、その A って何よ? って感じのエラーでしょうか。
つまり、表示する A にあたるものを宣言してやる必要があります。
それは hoge() の ()カッコ中に書きます。
たとえば hoge("Good Bye!") とすると


結果として Good Bye! が表示されました。
コレが引数です。あらかじめ、関数内で後に宣言するであろう事を仮置きしておき、hoge(引数) とすることで、関数にその引数が渡されます。
この引数は一つに限られません。たとえば、

-------------------------------------------------------
def hoge(A, B):
    print A + " and " + B
-------------------------------------------------------
とします。引数としてAとBの二つを与えると、結果として A + " and " + B を返すということですが、この + は文字を足すってことです。
ここで、

hoge("johnson", "johnson")

とすると・・・


となり johnson and johnson を得ます。 hoge("Carl", "Lewis") とすると、


Carl and Lewis を結果として得ます。

と前置きが長かったですが、要するに コピー元のファイルパス と コピー先のファイルパス を引数とするコピーするための関数をつくろうって事です。
レンダリング結果を指定した場所にコピーする というものを作ろうと思います。

nukeでは連番を扱うことが多いので、ファイル単体をコピーというよりはフォルダごとのコピーを紹介します。
で、pythonでその「フォルダごとコピー」をするには copytree() という関数を用います。copytree() を実行するには shutil というライブラリ(モジュール)を読み込む必要があり、そういう場合は

import shutil

と最初に宣言すれば shutil 内で定義されてる関数を用いることが出来ます。実は、これは標準ですでに存在する .py なのですが、どうやら、プログラムというものは全てのライブラリ(モジュール)を一旦読み込んできて、それで実行コードを展開していくというものではなく、必要なライブラリだけ必要に応じて読んできて・・・ っていうものであるらしいです。


こんな感じで、pythonのインストールされたフォルダの Lib フォルダに shutil.py は保存されています。どうやら、pythonはディフォルトでこの Lib フォルダの中身を読む準備が出来ているようです。いわゆる環境パスってヤツでしょうか。
でこの shutil.py の中身ですが・・・


こんな感じで、def ~~(): で定義された関数が複数個収録されてます。実際は import shutil をして、続きは

-------------------------------------------------------
import shutil
shutil.copytree(src, dst)
-------------------------------------------------------

と書きます。そうすると src(ファイルパス) で指定されたファイルが dst(ファイルパス) にコピーされます。
pythonのディレクトリのLibに保存されている(もしくはpythonがデフォルトで読みにいけるディレクトリに保存されている) shutil.py の中で定義されてる、copytree() という関数に、引数として、コピー元としてsrc(ファイルパス)と、コピー先としてdst(ファイルパス)を与える ということです。

そこで、たとえばnuke内から

-------------------------------------------------------
import shutil
shutil.copytree(r'C:/render/111205', r'//192.168.1.5/z/works/hoge/render/111205_copy')
-------------------------------------------------------

を実行してみます。


すると、


指定したディレクトリにファイルがコピーされました。(WindowsのインターフェイスがXPになったり、Win7になったりなのは、このブログを家で書いたり、会社で書いたりしているせいです・・・)

でたとえば、コレをつかって、Writeノード で自動的にレンダリング結果を指定したディレクトリにコピーするという関数を作ってみたいと思います。

この関数はWriteノードから実行するので、レンダリング結果のファイルパスは

nuke.thisNode().knob('file').value()

であらわせます。

-------------------------------------------------------
import shutil

def rendercopy(dst):
    shutil.copytree(nuke.thisNode().knob('file').value(), dst)
-------------------------------------------------------
※dstは引数

であらわせます。これを .nuke((ドット)nuke) フォルダにたとえば mytool.py として保存します。nukeは起動時に .nukeフォルダにある .py を読み込んでいます。なのでWriteノードの「after render」には

-------------------------------------------------------
 import mytool;mytool.rendercopy( コピー先のディレクトリ )
-------------------------------------------------------

とします。実際には以下のような感じです。


本来ならば

-------------------------------------------------------
import mytool
mytool.rendercopy(r'//192.168.1.5/z/works/hoge/render/111205_copy')
-------------------------------------------------------

です。 ただ、このWriteノードの「after render」では改行がきかないので改行部分に ;(セミコロン) を入れてます。

menu.py であらかじめこの.pyをインポートして関数を使えるようにしておけば、最初の import hoge のくだりは不要で、

-------------------------------------------------------
mytool.rendercopy( コピー先のディレクトリ )
-------------------------------------------------------

だけの記述で動きます。
ただ、今回、連番でレンダリングの出力先が


というようになっていて現状の、rendercopy() で取得する、nuke.thisNode().knob('file').value() だと返ってくるファイルパスが 「C:/render/test_%04d.jpg」 となります。
shutil.copytree() ではフォルダを指定しないといけないので、実際には「C:/render」をshutil.copytree()の一個目の引数として渡してやる必要があります。
なので、 「C:/render/test_%04d.jpg」 を 「C:/render」に変えてやる必要があります。
そこで、
-------------------------------------------------------
import re

src = nuke.thisNode().knob('file').value()
srcSplit = src .split('/')
src = src.replace(srcSplit[-1], "")
-------------------------------------------------------

とします。reモジュールの中のreplace()という関数を使ってます。

replaceは
-------------------------------------------------------
hoge = "I want a pineapple"
hoge = hoge.replace("a pineapple", "an apple")
-------------------------------------------------------

とすると hoge = "I want a pineapple" で定義された hoge を print hoge をすると


replace(src, dst) によって src を dst に置き換えられたということになります。
 なので、srcでファイルパスを定義したら、最後の「test_%04d.jpg」を取り除くために、

-------------------------------------------------------
src = nuke.thisNode().knob('file').value()
srcSplit = src .split('/')
-------------------------------------------------------

としてやって /(スラッシュ) ごとに文字列を分けていってリスト化します。
そのリストの最後が「test_%04d.jpg」となるはずなので、それは、

-------------------------------------------------------
src = nuke.thisNode().knob('file').value()srcSplit = src .split('/')
print srcSplit[-1]
-------------------------------------------------------


とすると、「test_%04d.jpg」が返ってくるはずです。
なので、ここで、さっき説明したreplace()を用いて、入れ替える文字列を無し("")にしてやると

-------------------------------------------------------
import re


src = nuke.thisNode().knob('file').value()
srcSplit = src .split('/')
src = src.replace(srcSplit[-1], "")
-------------------------------------------------------

とすれば、当初  「C:/render/test_%04d.jpg」 だった src が 「C:/render/」 に変わります。

なので、mytool.pyを

-------------------------------------------------------
import nuke
import shutil
import re

def rendercopy(dst):
    src = nuke.thisNode().knob('file').value()
    srcSplit = src .split('/')
    src = src.replace(srcSplit[-1], "")
    shutil.copytree(src, dst)
-------------------------------------------------------

と修正してセーブしてnukeを立ち上げなおしてWriteノードを実行させます。

そうすると、レンダリング終了後、このWriteノードの「after render」で指定した
-------------------------------------------------------
 import mytool;mytool.rendercopy( r'//192.168.1.5/z/works/hoge/render/111205_copy' )
-------------------------------------------------------

//192.168.1.5/z/works/hoge/render/111205_copy

に 中身がコピーされていました。

とこんな感じで、nuke内のパラメーター、もしくはユーザーの指定のパラメーターを引数として自分のやりたい機能を関数化をしてやるとWriteノードのpythonタブから用いることが出来ます。

レンダリング終了後にメールを送るとかもできますね。(特にいらないけど・・・)

2 comments:

  1. ファイル名・ディレクトリ名を取得したい場合は
    os.pathを使うと、便利かもしれません:)


    # ファイル名の取得
    import os
    src = os.path.basename( "C:/render/test_%04d.jpg" )

    # result : test_%04d.jpg


    # ディレクトリ名の取得
    import os
    src = os.path.dirname( "C:/render/test_%04d.jpg" )

    # result : C:/render

    ReplyDelete
  2. お、ありがとうございます!!
    こっちのほうがいいですね。さっそくw

    ReplyDelete