.NETとの相互運用性
IronPythonを設計するうえでの難題の1つが、この言語の対象者層が2つに分かれていて、それぞれの要求を満たす必要があるということです。すなわち、C言語ベースの標準のPythonにできるだけ近い動作を実現しつつ、.NET Frameworkとの相互運用性も厳密に実現しなくてはなりません。時には、この2つの要求が相反することもあります。たとえば、次のコードはどのように動作するのが適切でしょうか。
s = 'hello, world!' s.ToUpper()
Pythonでは、文字列型にToUpperメソッドがないので、このコードでは例外が発生するのが適切です。一方、.NETでは、この文字列に対してToUpper関数を呼び出した場合、"HELLO, WORLD!"が返るのが適切です。両者の要件は明らかに矛盾しています。
IronPythonでは、この問題に対処するために、既定では標準のPythonに準拠しつつ、開発者が明示的に指定した場合には.NETとの相互運用性を厳密に確保できるという方式を取り入れています。Pythonも.NETと同様、モジュールや名前空間といった単位でコードを取りまとめます。IronPythonにはclrという特別なモジュールがあり、このモジュールをインポートすると、.NETとの相互運用性を利用するという合図になります。次に示すIronPythonの対話型セッションは、import clrを使用した場合の例です。
>>> s = 'hello, world'
>>> s.ToUpper()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no
attribute 'ToUpper'
>>> import clr
>>> s.ToUpper()
'HELLO, WORLD'
import clrを呼び出していない場合、文字列に対してToUpperメソッドを呼び出すことはできません。Pythonの文字列オブジェクトにはToUpperメソッドがないため、IronPythonでもこのメソッドはないものとして扱われます。しかし、import clrを呼び出すと、文字列オブジェクトのすべてのメソッド(Pythonと.NETネイティブの両方)が利用可能になります。
clrモジュールは外部アセンブリの読み込みにも利用します。たとえば、.NETのXML処理クラス(XmlReaderやXmlDocumentなど)を利用するときには、System.Xml.dllへの参照を追加する必要があります。C#の場合、こうした参照の追加はコンパイル時に宣言的に行います。一方IronPythonの場合、コンパイル時というものがないため、参照の追加はコード内の命令によって行います。その後で、現在のスコープにクラスをインポートすれば、他のPythonオブジェクトと同様に扱えます。
import clr
clr.AddReference('System.Xml')
from System.Xml import XmlDocument
xml = XmlDocument()
xml.Load('http://devhawk.net/rss.aspx')
上のコードの処理内容は自明と思いますが、何点か補足しておきます。1つは、Pythonにはnewステートメントがない点です。型のインスタンスの生成は、型を関数のように呼び出すことで行います。もう1つは、クラスのインポートについてです。C#の場合は、usingステートメントを使って名前空間からクラスをインポートするのは、記述を簡略化するためのオプションの方法です。一方Pythonの場合、これは必須です。たとえば、次のように記述したコードは動作しません。
import clr
clr.AddReference('System.Xml')
# the following line doesn't work
xml = System.Xml.XmlDocument()
from...import形式のステートメントでは、短い名前をインポートします。名前空間の完全修飾名を使用するには、import System.Xml.XmlDocumentのように記述します。importでは、競合を回避するための名前の変更も可能です。その場合は、import <real name> as <new name>という形で記述します。
必要なアセンブリへの参照を追加し、型のインポートを行えば、.NET FrameworkのほぼすべてをIronPythonから利用できます。.NETの型のインスタンスを作成できるのはもちろん、.NETのイベントの利用、.NETのインターフェイスの実装、.NETの型の継承といったことも可能です。たとえば、次に示すコードはIronPythonのチュートリアルから引用したもので、Windowsフォームを利用しています。
# the winforms module in the tutorial directory import winforms from System.Windows.Forms import * from System.Drawing import * def click(*args): print args f = Form() f.Text = "My First Interactive Application" f.Click += click f.Show()
ただし、IronPythonからではできないことが1つあります。コードに属性を付加することです。.NET Frameworkのうち、WCFのコントラクトやXMLのシリアル化など、属性を必要とする部分については、IronPythonでは利用できません。こうしたライブラリはカスタム属性を基盤としています。カスタム属性とは、既存の静的型を拡張するためのカスタムのメタデータのように機能するものです。Pythonの場合、オブジェクトに静的型がないので、カスタム属性を付加する先がありません。
.NETの相互運用性に関してもう1つ述べておきたいのは、基本的に一方向の関係だということです。つまり、静的型付け言語で作成した.NETのクラスをPythonから呼び出すのは簡単ですが、動的型付け言語のオブジェクトを静的型付け言語から呼び出すのは、現時点では簡単ではありません。静的言語では、コンパイル時の型のメタデータを利用してメソッド呼び出しをディスパッチしますが、Pythonのような動的型付け言語では、そもそもメタデータが存在しません。もちろん、多言語というCLRの特性を考えると、静的型付け言語から動的型付け言語のコードを呼び出すという処理は、将来実現できればと思っています。
IronPythonの埋め込み
Pythonは、埋め込みが簡単な言語として注目を集めています。IronPythonも同様に、.NETアプリケーションに簡単に埋め込むことができ、スクリプトやマクロを利用した開発を実現できます。
IronPython 1.xでは、埋め込みの処理において、PythonEngine型を全面的に利用していました。次に示すのは、対話型のコンソールからPythonEngineを利用する例です。
>>> import clr
>>> clr.AddReference("IronPython.dll")
>>> from IronPython.Hosting import PythonEngine
>>> pe = PythonEngine()
>>> pe.Evaluate('2+2')
4
この例の場合は、ホストされているPython環境にホスト側との関連付けがないため、実行できるコードがかなり限られていますが、PythonEngineのGlobalsコレクションを使えば、アプリケーションのオブジェクトモデルをPython環境に対して公開できます。
IronPython 2.0では、ホストを行うためのコードは少々複雑になります。
import clr
clr.AddReference("IronPython.dll")
from IronPython.Hosting import PythonEngine
pe = PythonEngine.CurrentEngine
scope = pe.CreateScope()
source = pe.CreateScriptSourceFromString('2+2')
result = source.Execute(scope)
IronPython 2.0のコードが複雑になった理由の1つは動的言語ランタイム(DLR)です。DLRはCLRの拡張で、動的言語に必要な共通機能を提供します。この中には、共通のホスティングAPIも用意されています。DLRをホストするアプリケーションが、DLRをターゲットとした任意の言語をサポートできるようにするAPIです。つまり、DLRのホスティングAPIを利用したアプリケーションでは、IronPythonだけでなく、IronRubyやManaged JavaScriptなど、DLRを基盤としたサードパーティ製の任意の言語をサポートできます(詳しくはホスティングAPIの説明を参照)。
空白の使い方から動的型付けに至るまで、Pythonでの開発は、C#やVBとは大きく異なるのは確かです。私自身、C#からPythonに転向して日が浅いので、最初は奇妙な感じがするというのは大いに共感できます。しかし、そこを通り過ぎれば、Pythonの生産性の高さに目を見張ることと思います。
