リストっぽい何か

__getitem__と__setitem__を定義すると、リストみたいに[]を使って要素を参照したり代入したりできます。

class Foo:
  def __init__(self, filename):
    self.file = file(filename, 'r+')

  def __setitem__(self, index, value):
    self.file.seek(index)
    self.file.write(value)

  def __getitem__(self, index):
    self.file.seek(index)
    return self.file.read(1)

f = Foo('list.txt')

a = 'apple'
for i in range(len(a)):
  f[i] = a[i]

for i in range(len(a)):
  print f[i]

実行結果は以下のようになります。

a
p
p
l
e

最後のforループがかっこ悪いので改良してみました。

class Foo:
  def __init__(self, filename):
    self.file = file(filename, 'r+')
    self.len = 0

  def __setitem__(self, index, value):
    self.len += 1
    self.file.seek(index)
    self.file.write(value)

  def __getitem__(self, index):
    self.file.seek(index)
    return self.file.read(1)

  def __len__(self):
    return self.len

  def __iter__(self):
    self.i = 0
    return self

  def next(self):
    if self.i == self.len:
      raise StopIteration
    else:
      r = self[self.i]
      self.i += 1
      return r

f = Foo('list.txt')

a = 'apple'
for i in range(len(a)):
  f[i] = a[i]

# かっこ悪いオリジナル
for i in range(len(a)):
  print f[i]

# __len__が定義されているのでlenが使える
for i in range(len(f)):
  print f[i]

# イテレータを返すのでforにそのまま渡せる
for i in f:
  print i

比較

インスタンスの比較をしてみます。

class Foo:
  def __init__(self, name):
    self.name = name
    self.len = len(name)

  def __lt__(self, other):
    return self.len < other.len

  def __le__(self, other):
    return self.len <= other.len

  def __eq__(self, other):
    return self.name == other.name

  def __ne__(self, other):
    return self.name != other.name

  def __gt__(self, other):
    return self.len > other.len

  def __ge__(self, other):
    return self.len >= other.len

a = Foo('work')
b = Foo('lover')

print a < b, a.__lt__(b)   # => True True
print a <= b, a.__le__(b)  # => True True
print a == b, a.__eq__(b)  # => False False
print a != b, a.__ne__(b)  # => True True
print a > b, a.__gt__(b)   # => False False
print a >= b, a.__ge__(b)  # => False False

比較演算を行うと、比較演算子に対応したメソッドが呼ばれます。これらが定義されていない場合、__cmp__を見にいきます。__cmp__を定義するときは、自分が相手より小さい場合は負の値を、同じ場合は0を、自分が相手より大きい場合は正の値を返すようにする、という決まりがあります。

class Foo:
  def __init__(self, name):
    self.name = name
    self.len = len(name)

  def __cmp__(self, other):
    if self.len < other.len:
      return -1
    elif self.len > other.len:
      return 1
    else:
      return 0

a = Foo('work')
b = Foo('lover')

print a < b   # => True
print a <= b  # => True
print a == b  # => False
print a != b  # => True
print a > b   # => False
print a >= b  # => False

print a.__cmp__(b)  # => -1

a = Foo('orange')
b = Foo('banana')

print a < b   # => False
print a <= b  # => True
print a == b  # => True
print a != b  # => False
print a > b   # => False
print a >= b  # => True

print a.__cmp__(b)  # => 0

比較演算子が__cmp__を呼び出したときは、結果に応じて勝手にTrueとFalseにしてくれるみたいです。


__cmp__も定義されていない場合はインスタンスのアドレスにより比較が行われます。

class Foo:
  def __init__(self, name):
    self.name = name
    self.len = len(name)

a = Foo('work')
b = Foo('lover')

print a < b   # => False
print a <= b  # => False
print a == b  # => False
print a != b  # => True
print a > b   # => True
print a >= b  # => True

print id(a), id(b) # => -1211313844 -1211313940