2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

How python classes and magic methods are working.

Posted at

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__はダンダー レンと発音する
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?