シンプルな記述が可能
C#で開発を進める中で少々イライラするのが、かなり構造化した記述を強いられるという点です。こうした構造には、静的型付けに関連するものもあります。変数の型を指定したり、演算子をキャストしたりなどです。しかし、もう1つ言えるのは、C#にはスタンドアロン関数がなく、ごく短いコードを簡単なスクリプトとして記述するのは不可能だという点です。すべての関数はオブジェクトに所属させる必要があります。このため、C#で"Hello, World!"のプログラムをごく簡単に実装しても、次のようなコードになってしまいます。
class Program
{
static void Main()
{
System.Console.WriteLine("Hello, World");
}
}
これとは対照的に、Pythonなら次のコードで済みます。
print "Hello, World"
もちろん、"Hello, World"の実装だけで言語のすべてを判断できるわけではありませんが、ここで言いたいのは、Pythonは処理の複雑さに応じた拡張性を備えているということです。簡単なスクリプトを記述するだけなら、クラスや関数などの不要な構造をコードに持ち込む必要はありません。関数を作りたければ、スタンドアロンで作成できます。所属先のクラスを無理に見つける必要はありません(先ほどの例で登場したbubble_sort関数もそうでした)。クラスやオブジェクトが本当に必要であれば作成できますが、無意味にそんな手間をかける必要はありません。
多態性に関する相違点
異なる型を一律に処理する多態性は、オブジェクト指向プログラミングには欠かせない機能です。静的型付け言語には、継承、インターフェイス、ジェネリック型など、多態性を実現するためのさまざまな仕組みがあります。一方Pythonは、動的型付け言語であるため、こうした仕組みがなくても多態性を実現できます(Pythonにも継承はありますが、多態性を実現するためではありません)。Pythonの場合、型の宣言ではなく、型が持つ機能に基づいて、型の互換性が実行時に判断されます(この処理は一般に「ダックタイピング(duck typing)」と呼ばれています)。この機能のおかげで、柔軟性と再利用性の高いコードを多種多様な状況で活用できます。
その具体例として、先ほどのbubble_sort関数をもう一度見てみましょう。この関数のパラメータは、並べ替えの対象となるコレクションを表すarだけです。コードをよく見るとわかるように、このbubble_sort関数でこのパラメータが役割を果たすためには、オブジェクトのインスタンスarに次の3つの機能が必要です。
- arオブジェクトの長さを取得できること。
- arオブジェクトから、指定した数値インデックスの項目を取得できること。
- arオブジェクトに対し、指定した数値インデックスの項目を設定できること。
これと同等なバブルソートのメソッドをC#で実装するとしたら、上記の3つの機能を実装した特定の型(インターフェイスまたは基本クラス)を使い、arパラメータはその型として宣言する必要があります。そのバブルソート処理を呼び出すコードでは、その型を実装したパラメータを渡しているかどうかがコンパイル時に検証されます。それ以外の型をパラメータとして渡すと、コンパイルエラーになります。たとえ、実際に渡している型が、3つの機能をすべて実装していたとしてもです。
Pythonでは、型のチェックはすべて実行時に行われます。このため、arパラメータに渡した型のインスタンスに上記の3つのメソッドのいずれかが実装されていない場合は、バブルソートのコードの実行時にTypeErrorが発生します。バブルソートのコードの場合、必要な3つのメソッドには特別な名前が付いています。コレクションの名前を返すのは__len__、インデックスに応じた値を取得するのは__getitem__、値を設定するのは__setitem__です。Pythonには、さまざまな状況で使用する特別な名前が多数定義されています。
次に示すのは、カスタムの連結リストのクラスの例です。これら3つのメソッドを実装しているので、先ほどのバブルソートのコードでも利用できます。
class linked_list(object):
class node(object):
def __init__(self,value):
self.data = value
self.next = None
def __init__(self):
self.head = None
def insert(self, value):
n = linked_list.node(value)
n.next = self.head
self.head = n
def __iter__(self):
cur = self.head
while cur != None:
yield cur
cur = cur.next
def __len__(self):
count = 0
for n in self:
count += 1
return count
def find_node(self, key):
cur = self.head
for x in range(key):
cur = cur.next
return cur
def __getitem__(self, key):
return self.find_node(key).data
def __setitem__(self, key, value):
self.find_node(key).data = value
もちろん、連結リストでは、インデックスを指定してメンバにアクセスする機能はないのが一般的です。しかし肝心なのは、このカスタムクラスについては、__len__、__getitem__、__setitem__を実装しているので、インスタンスをそのままbubble_sort関数に渡せるという点です。
