How python classes and magic methods are working.


# Abstruction

`__init__`, `__len__`などPythonには__methodanme__で表現されるメソッドがあります。Pythonは、__ のメソッドによって動いています。これらがどのようにPythonの中で動いているかを説明します。Pythonをどう使うかよりも、Pythonがどのように動いているか理解するための記事です。


# Introduction of `__`

まず、Pythonでオブジェクトの長さを取得したいときは`len`を使います。下記の例のように書くことができます。


```python
>>> len("hello")
5
>>> len([1, 2, 3])
3
>>> len({'a': 1, 'b': 2})
2
>>> len((1, 2, 3))
3
```

実際にこれがどのように動いているか?

```python
>>> "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`を実装してみましょう。

```python
>>> 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が２つあることから、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を使うことによって実現されます。

```python
>>> 1 + 20
21
>>> (1).__add__(20)
21
```
`object[index]`で呼び出した際は、dunder getitemが呼ばれます。

```python
>>> "hello"[0]
'h'
>>> "hello".__getitem__(0)
'h'
```

# 自分でクラスを定義する
Pythonは dunder methodを使うことにより、動いていきます。実際に自分でクラスを定義し、さらに理解を深めます。

### Dog classを定義

```
class Dog:

```

### dunder init でコンストラクタを定義
今回は、name とheightを持つとします。それぞれ sunder name とsunder heightに代入されます。

```python
    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'
```


### コード全体

```python
'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__`はダンダー レンと発音する