Abstruction
__init__
, __len__
などPythonには__methodanme__で表現されるメソッドがあります。Pythonは、__ のメソッドによって動いています。これらがどのようにPythonの中で動いているかを説明します。Pythonをどう使うかよりも、Pythonがどのように動いているか理解するための記事です。
Introduction of __
まず、Pythonでオブジェクトの長さを取得したいときはlen
を使います。下記の例のように書くことができます。
>>> len("hello")
5
>>> len([1, 2, 3])
3
>>> len({'a': 1, 'b': 2})
2
>>> len((1, 2, 3))
3
実際にこれがどのように動いているか?
>>> "hello".__len__() # string
5
>>> [1, 2, 3].__len__() # list
3
>>> {'a': 1, 'b': 2}.__len__() # dict
2
>>> (1, 2, 3).__len__() # tuple
3
中の__len__()
を呼び出すことによってオブジェクトの長さを取得できます。すなわち関数len()
はオブジェクトの__len__
を呼び出していることとなります。Pythonの関数よりも、オブジェクト自体がどのような機能を持っているかに従い動きます。
上記を確かめるためにmylen
を実装してみましょう。
>>> def mylen(object):
... 'Return length of the object'
... return object.__len__()
...
>>> mylen("hello")
5
>>> mylen([1, 2, 3])
3
>>> mylen({'a': 1, 'b': 2})
2
>>> mylen((1, 2, 3))
3
__membername__
の呼び方
__len__
はどのように発音するのでしょうか?
# まずは、記号一つ一つ正確に発音していきましょう。
__len__
=> underbar underbar len underbar underbar # 長い!
# 最後のunderbar underbarを省略しましょう。
=> underbar underbar len # 長い!!
# 最初にunderbarが2つあることから、doubleとつけることにしましょう。
=> double underbar len # いや、まだ長い!!!
# double とunderbarを結合し新しい呼び方にします。
=> dunder len
# ということで最終的に dunder len ダンダー レンと発音します。
# これと同じように、privateを慣習的に表す`_membername`も
_foo
=> single underbar foo
# 同じようにくっつける
=> sunder foo
# sunder foo サンダー フーと発音します。
その他の dunder の例
オブジェクトのdunder memberを呼び出すことによってPythonは動いています。
たとえば、+
オペレータを使用した場合でもこのdunder methodを使うことによって実現されます。
>>> 1 + 20
21
>>> (1).__add__(20)
21
object[index]
で呼び出した際は、dunder getitemが呼ばれます。
>>> "hello"[0]
'h'
>>> "hello".__getitem__(0)
'h'
自分でクラスを定義する
Pythonは dunder methodを使うことにより、動いていきます。実際に自分でクラスを定義し、さらに理解を深めます。
Dog classを定義
class Dog:
dunder init でコンストラクタを定義
今回は、name とheightを持つとします。それぞれ sunder name とsunder heightに代入されます。
def __init__(self, name, height):
self._name = name
self._height = height
メソッド関数 bark
こちらは、dunderを使ったものではなく通常のメソッドです。
def bark(self):
print 'Woof! %s is barking' % self._name
barkはこのように呼び出すことが可能です。
>>> dog = Dog("foo", 123)
>>> dog.bark()
Woof! foo is barking
dunder len
今回 Dogインスタンスの長さはコンストラクタで渡されたheightを返します。
def __len__(self):
return height
このようにbuild-in関数のlen
で呼ばれます。
>>> dog = Dog("foo", 123)
>>> len(dog)
123
>>> dog.__len__()
123
dunder getitem
今回は、dogのインスタンスのlength(今回の場合であれば、sunder height)より大きければ、IndexError
を起こすことにします。
返り値は、indexに111をかけた値とします。
def __getitem__(self, index):
if index >= len(self):
raise IndexError('Oops, index out of range')
return index * 111
Dog[index]で呼び出すことができます。
>>> dog = Dog("foo", 123)
>>> dog[1]
111
>>> dog[2]
222
>>> dog[234]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "class_demo.py", line 18, in __getitem__
raise IndexError('Oops, index out of range')
IndexError: Oops, index out of range
>>> dog.__getitem__(1)
111
>>> dog.__getitem__(2)
222
>>> dog.__getitem__(234)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "class_demo.py", line 18, in __getitem__
raise IndexError('Oops, index out of range')
IndexError: Oops, index out of range
dunder call
Dogインスタンスが()が呼び出された時に実行される関数を定義します。
def __call__(self, action):
if action == 'fetch':
return '%s is fetching' % self.name
elif action == 'owner':
return 'Giwa'
else:
raise ValueError('Unknown action')
Dog("string") で呼び出すことができます。
>>> dog = Dog("foo", 123)
>>> dog('fetch')
'foo is fetching'
>>> dog('owner')
'Giwa'
>>> dog('name')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "class_demo.py", line 27, in __call__
raise ValueError('Unknown action')
ValueError: Unknown action
>>> dog.__call__('fetch')
'foo is fetching'
>>> dog.__call__('owner')
'Giwa'
>>> dog.__call__('name')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "class_demo.py", line 27, in __call__
raise ValueError('Unknown action')
ValueError: Unknown action
dunder add
Dog インスタンス同士がたされた場合、両方のnameとheightを足しあわせて、それらを引数にし、新しいDogインスタンスを作ります。
def __add__(self, other):
newname = self._name + '~' + other._name
newheight = self._height + other._height
return Dog(newname, newheight)
- のオペレータでdunder addが呼び出されます。
>>> dog1 = Dog("foo", 10)
>>> dog2 = Dog("bar", 20)
>>> dog3 = dog1 + dog2
>>> dog3.bark()
Woof! foo~bar is barking
>>> len(dog3)
30
>>> dog4 = dog1.__add__(dog2)
>>> dog4.bark()
Woof! foo~bar is barking
>>> len(dog4)
30
dunder str
str()
もしくは print
でよばれた時に使われるものです。
def __str__(self):
return 'I am a dog named %s' % self._name
このように呼ばれます。
>>> dog = Dog('foo', 123)
>>> str(dog)
'I am a dog named foo'
>>> print dog
I am a dog named foo
>>> dog.__str__()
'I am a dog named foo'
コード全体
'Demonstrate how Python classes and magic methods work'
class Dog:
'A simple canine class'
def __init__(self, name, height):
self._name = name
self._height = height
def bark(self):
print 'Woof! %s is barking' % self.name
def __len__(self):
return self._height
def __getitem__(self, index):
if index >= len(self):
raise IndexError('Oops, index out of range')
return index * 111
def __call__(self, action):
if action == 'fetch':
return '%s is fetching' % self._name
elif action == 'owner':
return 'Giwa'
else:
raise ValueError('Unknown action')
def __add__(self, other):
newname = self._name + '~' + other._name
newheight = self._height + other._height
return Dog(newname, newheight)
def __str__(self):
return 'I am a dog named %s' % self._name
まとめ
- dunder methodを幾つかを例をあげて説明しました
- Pythonは dunder methodによって動いている
-
_foo
は サンダー フーと発音する -
__len__
はダンダー レンと発音する