Last active
April 12, 2016 03:40
-
-
Save Cartman0/182f6bd4e59d4a399134336fdbdce82b to your computer and use it in GitHub Desktop.
Dive Into Python3 AppnedixBメモ(特殊メソッド名)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"metadata": { | |
"toc": "true" | |
}, | |
"cell_type": "markdown", | |
"source": "# Table of Contents\n <p><div class=\"lev1\"><a href=\"#特殊メソッド名-1\"><span class=\"toc-item-num\">1 </span>特殊メソッド名</a></div><div class=\"lev2\"><a href=\"#飛び込む-1.1\"><span class=\"toc-item-num\">1.1 </span>飛び込む</a></div><div class=\"lev2\"><a href=\"#基礎-1.2\"><span class=\"toc-item-num\">1.2 </span>基礎</a></div><div class=\"lev2\"><a href=\"#イテレータのように振る舞うクラス-1.3\"><span class=\"toc-item-num\">1.3 </span>イテレータのように振る舞うクラス</a></div><div class=\"lev2\"><a href=\"#算出属性-1.4\"><span class=\"toc-item-num\">1.4 </span>算出属性</a></div><div class=\"lev2\"><a href=\"#関数のように振る舞うクラス-1.5\"><span class=\"toc-item-num\">1.5 </span>関数のように振る舞うクラス</a></div><div class=\"lev2\"><a href=\"#シーケンスのように振る舞うクラス-1.6\"><span class=\"toc-item-num\">1.6 </span>シーケンスのように振る舞うクラス</a></div><div class=\"lev2\"><a href=\"#辞書のように振る舞うクラス-1.7\"><span class=\"toc-item-num\">1.7 </span>辞書のように振る舞うクラス</a></div><div class=\"lev2\"><a href=\"#数値のように振る舞うクラス-1.8\"><span class=\"toc-item-num\">1.8 </span>数値のように振る舞うクラス</a></div><div class=\"lev2\"><a href=\"#比較可能なクラス-1.9\"><span class=\"toc-item-num\">1.9 </span>比較可能なクラス</a></div><div class=\"lev2\"><a href=\"#シリアライズ可能なクラス-1.10\"><span class=\"toc-item-num\">1.10 </span>シリアライズ可能なクラス</a></div><div class=\"lev2\"><a href=\"#withブロックで使えるクラス-1.11\"><span class=\"toc-item-num\">1.11 </span>withブロックで使えるクラス</a></div><div class=\"lev2\"><a href=\"#完全に奥義的なもの-1.12\"><span class=\"toc-item-num\">1.12 </span>完全に奥義的なもの</a></div><div class=\"lev2\"><a href=\"#参考リンク-1.13\"><span class=\"toc-item-num\">1.13 </span>参考リンク</a></div>" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "- [Dive Into Python3 1章メモ](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/d54093a99b9254c81bf1123adacbc48a/raw/eedc90bbbfc14e259854f2e739fffeec4cb4d8f7/DiveIntoPython3_01.ipynb)\n- [Dive Into Python3 2章メモ(ネイティブデータ型)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/988b51d8482ad9ade835bb07efdffb38/raw/784cf276b7cebe254e59f09dcea6f09eea760d38/DiveIntoPython3_02.ipynb)\n- [Dive Into Python3 3章メモ(内包表記)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/183ec6f6c835f621106f7c27d215290a/raw/bd7677946d6400bbe6acd257df7fb9c5976c3320/DiveIntoPython3_03.ipynb)\n- [Dive Into Python3 4章メモ(文字列)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/bb974f82e8a3fc74ac82c3c2a5b1a4f9/raw/63a80011c9391b451108c1a4dc804ec5ff125f34/DiveIntoPython3_04.ipynb)\n- [Dive Into Python3 5章メモ(正規表現)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/b834807a0dabb1c458b87be2f333a5ca/raw/37d6f25f67e017a569e1f0b60a9fcbe49d50515f/DiveIntoPython3_05.ipynb)\n- [Dive Into Python3 6章メモ(クロージャとジェネレータ)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/a8998f8f88c5578271495ada56cc4809/raw/4e9b8ef3543016a710f905215693694acd703549/DiveIntoPython3_06.ipynb?flush_cache=true)\n- [Dive Into Python3 7章メモ(クラスとイテレータ)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/8eb511c3b2aa6cadad6f6d49666c9db2/raw/60863d1ce1c1e9e4cb336ad79a0e252807beb87c/DiveIntoPython3_07.ipynb?flush_cache=true)\n- [Dive Into Python3 8章メモ(高度なイテレータ)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/9ed3037cbdac59af53c20c125aed806e/raw/46ee3c71d72ab7ee665cf0c10f8717734632b462/DiveIntoPython3_08.ipynb)\n- [Dive Into Python3 9章メモ(ユニットテスト)](http://nbviewer.jupyter.org/urls/gist.githubusercontent.com/Cartman0/1833320ab55ae5ea660ce0c635483d26/raw/46034833206366c8011594cbea2efce90168d54b/DiveIntoPython3_09.ipynb)\n- [Dive Into Python3 10章メモ(リファクタリング)](http://nbviewer.jupyter.org/gist/Cartman0/0c0af20eb95236da4da9547258fe2bb3)\n- [Dive Into Python3 11章メモ(ファイル)](http://nbviewer.jupyter.org/gist/Cartman0/152eacd23b793ecd3dbf5f706ae33e92)\n- [Dive Into Python3 12章メモ(XML)](http://nbviewer.jupyter.org/gist/Cartman0/87d2d00c5d64c66778f20d1d056e76c7)\n- [Dive Into Python3 13章メモ(Pythonオブジェクトをシリアライズ)](http://nbviewer.jupyter.org/gist/Cartman0/33cd21bbc84ae3c07ea84884f8354ab3)\n- [Dive Into Python3 14章メモ(HTTPウェブサービス)](http://nbviewer.jupyter.org/1c885ad8ce22c021b0350f53ee3a1126/DiveIntoPython3_14.ipynb)\n- [Dive Into Python3 16章メモ(Pythonライブラリをパッケージ化)](http://nbviewer.jupyter.org/gist/Cartman0/4b95fdbe00b52f4d0c90a1654a4b15c7)" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "# 特殊メソッド名" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 飛び込む" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "特殊メソッドを使うことで、自作のクラスをシーケンスのように振る舞せたり、\n関数のように振る舞せたり、イテレータのように振る舞せたりすることができる。\n数値として振る舞わせることさえできる。\nこのAppendixは、すでに目にした特殊メソッドのレファレンスを提供するとともに、より奥義的な特殊メソッドの一部を手短に紹介する。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 基礎" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "基礎的な特殊メソッドは他にもいくつか存在し、それらは自作のクラスをデバッグするときに特に便利。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__init__()`メソッドは、インスタンスが作成された後に呼び出される。もし実際の生成プロセスを制御したい場合は`__new__()`メソッドを使用する。\n\nインスタンスを初期化する:\t\n\n`x = MyClass()`\n\n内部: `x.__init__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__repr__()`メソッドは、有効なPythonの式を表す文字列を返すべき。\n\n文字列としての「正式な」表現:\n\n`repr(x)`\n\n内部: `x.__repr__()`\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__str__()`メソッドは、`print(x)`するときにも呼び出される。\n\n文字列としての「インフォーマルな」値:\n\n`str(x)`\n\n内部: \n`x.__str__()`\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "bytes型の導入にともない、Python 3で新たに加わった。\n\nバイト列としての「インフォーマルな」値:\n\n`bytes(x)`\n\n内部: `x.__bytes__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "慣例により、format_specはFormat Specification Mini-Languageに従うべきである。\nPython標準ライブラリのdecimal.pyは、独自の`__format__()`メソッドを持っている\n\nフォーマットされた文字列としての値:\n\n`format(x, format_spec)`\n\n内部: `x.__format__(format_spec)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## イテレータのように振る舞うクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__iter__()`メソッドは、新しいイテレータを作るときに呼び出される。\nこのメソッドは、イテレータの初期値を設定するのに適した場所。\n\nシーケンスをイテレートする:\n\n`iter(seq)`\n\n内部: \n`seq.__iter__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__next__()`メソッドは、イテレータから次の値を取得するときに呼び出される。\n\nイテレータから次の値を取得する:\n\n`next(seq)`\t\n\n内部: `seq.__next__()`\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__reversed__()`メソッドはあまり使われない。\nこれは、既存のシーケンスを受け取って、そのシーケンスの要素を逆順に(末尾から先頭へ)生み出すイテレータを返す。\n\n逆順のイテレータを作る:\n\n`reversed(seq)`\n\n内部: `seq.__reversed__()`\n\n参考:http://docs.python.jp/3/reference/datamodel.html#object.__reversed__" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "forループはイテレータに従って役目を果せる。以下のループでは:\n\n```\nfor x in seq:\n print(x)\n```\n\nPython 3 は、`seq.__iter__()` を呼び出してイテレータを作り、\n次にイテレータの `__next__()`メソッドを呼び出して各々のxの値を取得する。\n`__next__()`メソッドが `StopIteration`例外を送出すると、\nforループは静かに終了する。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 算出属性" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスが `__getattribute__()`メソッドを定義している場合は、すべての属性名/メソッド名に対するすべての参照においてPythonがこのメソッドを呼び出す(ただし特殊メソッド名は、不快な無限ループを引き起こすので除外される)。\n\n算出属性を取得する(無条件):\n\n`x.my_property`\t\n\n内部: `x.__getattribute__('my_property')`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスが `__getattr__()`メソッドを定義している場合は、通常通りのすべての場所から属性を探したあとに限って、\nPythonがこのメソッドを呼び出す。\nインスタンスxが属性colorを定義している場合には、x.colorは`x.__getattr__('color')` を呼び出さない。\nこれは、すでに定義された `x.color` の値を返すだけだ。\n\n算出属性を取得する(フォールバック):\n\n`x.my_property`\n\n内部:`x.__getattr__('my_property')`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__setattr__()`メソッドは、属性に値を代入しようとするときに呼び出される。\n\n属性を設定する:\n\n`x.my_property = value`\n\n内部:\n`x.__setattr__('my_property', value)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__delattr__()`メソッドは、属性を削除しようとするときに呼び出される。\n\n属性を削除する:\n\n`del x.my_property`\n\n内部: `x.__delattr__('my_property')`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__dir__()`メソッドは、`__getattr__()`メソッドや `__getattribute__()`メソッドを定義するときに便利。\n\n通常、`dir(x)` を呼び出すと、標準の属性とメソッドだけがリストアップされる。つまり、`__getattr()__`メソッドがcolor属性を動的に提供する場合は、`dir(x)` はcolorを利用可能な属性としてリストアップしてくれない。\n\n`__dir__()`をオーバーライドすることによって、color属性が利用可能な属性としてリストアップされるようできる。\nそうしておけば、クラスを使いたいと思う人にとって助けになる。さもないと、コードの内部を掘り起こさないといけなくなるかもしれない。\n\n\n全ての属性とメソッドをリストアップする:\n`dir(x)`\n\n内部: `x.__dir__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__getattr__()`メソッド と `__getattribute__()`メソッドの違いはわずかだが重要。\nこれは2つの例で説明できる:" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "1. 属性名は、`__getattr()__`メソッドに文字列として渡される。\nその名前が'color'であれば、このメソッドは値を返す(この例ではハードコードされた文字列を返しているだけだが、実際には、いくつかの計算などを行なって、その結果を返すのがふつうだ)。\n\n2.\t不明な属性名が与えられた場合、`__getattr()__`メソッドは `AttributeError`例外を発生させる必要がある。\nさもなければ、未定義の属性にアクセスしたときに、コードは密やかに誤動作を始めるだろう(詳しく言うと、このメソッドは、例外を送出しなかったり明示的に値を返さない場合にはPythonの無効値であるNoneを返すのだ。これは、明示的に定義されていないすべての属性値が None になることを意味するが、ほぼ確実に、それは望ましい振る舞いではない)。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Dynamo:\n def __getattr__(self, key):\n if key == 'color': # 1\n return 'PapayaWhip'\n else:\n raise AttributeError # 2\n \n \n\n \ndyn = Dynamo()", | |
"execution_count": 1, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "インスタンス`dyn` は `color` という名前の属性を持っていないので、\n`__getattr__()`メソッドが呼び出されて、算出された値が提供される。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dyn.color", | |
"execution_count": 2, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "'PapayaWhip'" | |
}, | |
"metadata": {}, | |
"execution_count": 2 | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dyn.color = 'LemonChiffon'", | |
"execution_count": 3, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`dyn.color` を明示的に設定した後は、もはや `__getattr__()`メソッドは `dyn.color` を提供するために呼び出されない。\nインスタンス上にすでに `dyn.color` が定義されているため。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dyn.color", | |
"execution_count": 4, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "'LemonChiffon'" | |
}, | |
"metadata": {}, | |
"execution_count": 4 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "その一方で、`__getattribute__()`メソッドは絶対的なものであり、無条件で使われる。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class SuperDynamo:\n def __getattribute__(self, key):\n if key == 'color':\n return 'PapayaWhip'\n else:\n raise AttributeError\n \n\n \ndyn = SuperDynamo()", | |
"execution_count": 5, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`dyn.color` の値を提供するために `__getattribute__()`メソッドが呼び出される。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dyn.color", | |
"execution_count": 6, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "'PapayaWhip'" | |
}, | |
"metadata": {}, | |
"execution_count": 6 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "明示的にdyn.colorを設定した後でさえも、`__getattribute__()`メソッドは依然として呼び出され、dyn.colorの値を提供する。\n`__getattribute__()`メソッドが存在する場合は、全ての属性とメソッドを探すために、無条件で呼び出される。\n属性がインスタンスの生成後に明示的に設定されている場合でさえも呼び出される。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dyn.color = 'LemonChiffon'\ndyn.color", | |
"execution_count": 7, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "'PapayaWhip'" | |
}, | |
"metadata": {}, | |
"execution_count": 7 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスに `__getattribute__()` を定義する場合は、\nおそらく `__setattr__()`メソッドも定義して、2つのメソッド間で属性値が追跡されるように連携させる。\n\nさもなければ、インスタンス作成後に設定した全ての属性は消えてしまうことになる。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true | |
}, | |
"cell_type": "markdown", | |
"source": "`__getattribute__()`メソッドにはさらなる注意を払う必要がある。\nこのメソッドは、Pythonがメソッド名を探すときにも呼び出されるため。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "このクラスは、常にAttributeError例外を送出する `__getattribute__()`メソッドを定義している。\n属性やメソッドの参照はすべて成功しなくなる。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Rastan:\n def __getattribute__(self, key):\n raise AttributeError\n \n def swim(self):\n pass\n \n", | |
"execution_count": 8, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "hero.swim()を呼び出すとき、PythonはRastanクラスからswim()メソッドを探し出す。\n全ての属性とメソッドの参照が `__getattribute__()`メソッドを通して行われるので、\nこの参照も `__getattribute__()`メソッドを通して行われる。\nこの場合は、`__getattribute__()`メソッドが `AttributeError`例外を送出するので、メソッドの参照は失敗し、\nしたがってこのメソッド呼び出しは失敗する。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "hero = Rastan()\nhero.swim() ", | |
"execution_count": 9, | |
"outputs": [ | |
{ | |
"evalue": "", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-9-bd649b032304>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mhero\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mRastan\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mhero\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mswim\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;32m<ipython-input-8-a640d3923d15>\u001b[0m in \u001b[0;36m__getattribute__\u001b[1;34m(self, key)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mRastan\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m__getattribute__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mAttributeError\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mswim\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mAttributeError\u001b[0m: " | |
], | |
"output_type": "error", | |
"ename": "AttributeError" | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "class Rastan:\n \n def swim(self):\n pass\n\nhero = Rastan()\nhero.swim()", | |
"execution_count": 10, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "dir(hero)", | |
"execution_count": 11, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "['__class__',\n '__delattr__',\n '__dict__',\n '__dir__',\n '__doc__',\n '__eq__',\n '__format__',\n '__ge__',\n '__getattribute__',\n '__gt__',\n '__hash__',\n '__init__',\n '__le__',\n '__lt__',\n '__module__',\n '__ne__',\n '__new__',\n '__reduce__',\n '__reduce_ex__',\n '__repr__',\n '__setattr__',\n '__sizeof__',\n '__str__',\n '__subclasshook__',\n '__weakref__',\n 'swim']" | |
}, | |
"metadata": {}, | |
"execution_count": 11 | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "hero.swim", | |
"execution_count": 12, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "<bound method Rastan.swim of <__main__.Rastan object at 0x000001960E3D7EF0>>" | |
}, | |
"metadata": {}, | |
"execution_count": 12 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 関数のように振る舞うクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__call__()`メソッドを定義すると、(関数が呼び出せるのと同じように)クラスのインスタンスを呼び出せるようにできる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "インスタンスを関数のように「呼び出す」:\n\n`my_instance()`\n\n内部: `my_instance.__call__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "[`zipfile`モジュール](http://docs.python.jp/3/library/zipfile.html) は、これを使って、暗号化されたzipファイルを与えられたパスワードで復号するクラスを定義している。\nzipの復号アルゴリズムは、復号を行っているあいだ、内部状態を保持する必要がある。\n復号器をクラスとして定義することによって、復号器クラスの個々のインスタンスの中に内部状態を保持できるようになる。\nこの状態は `__init__()`メソッドで初期化され、ファイルが復号されるにつれて更新される。\nしかし、このクラスは関数のように「呼び出し可能」でもあるので、そのインスタンスを `map()`関数の最初の引数として渡せるのだ:" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "1. `_ZipDecryptor` クラスは、3つの回転キーの形で状態を保持する。\nこれらは後に `_UpdateKeys()`メソッド(ここには示されていない)の中で更新される。\n\n2. このクラスは `__call__()`メソッドを定義している。\nこのメソッドは、クラスインスタンスを関数のように呼び出せるようにする。\nこの例での `__call__()`メソッドは、zipファイルの1つのバイトを復号し、復号されたバイトに基づいて回転キーを更新する。\n\n```\n# excerpt from zipfile.py\nclass _ZipDecrypter:\n.\n.\n.\n def __init__(self, pwd):\n self.key0 = 305419896 # 1\n self.key1 = 591751049\n self.key2 = 878082192\n for p in pwd:\n self._UpdateKeys(p)\n\n def __call__(self, c): # 2\n assert isinstance(c, int)\n k = self.key2 | 2\n c = c ^ (((k * (k^1)) >> 8) & 255)\n self._UpdateKeys(c)\n return c\n.\n.\n.\nzd = _ZipDecrypter(pwd) # 3\nbytes = zef_file.read(12)\nh = list(map(zd, bytes[0:12])) # 4\n```\n\n3.`zd` は `_ZipDecryptor`クラスのインスタンス。\n変数`pwd` は、 `__init__()`メソッドに渡され、そこで格納されて、回転キーの最初の更新に使われる。\n\n4.zipファイルの最初の12バイトが得られたら、\nそのバイトを `zd` にマッピングすることで復号を行う。\nこれは実質的には `zd` を12回「呼び出す」ことになり、\nそれは `__call__()`メソッドを12回呼び出し、それは内部状態を12回更新して結果のバイトを12回返す。\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## シーケンスのように振る舞うクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスが、値の集合のためのコンテナとして振る舞うのなら(つまり、そのクラスは値を「含んでいる」か?という質問が意味をなすのであれば)、\nおそらくそのクラスは、シーケンスとして振る舞えるようにするための特殊メソッドを定義すべき。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "シーケンスの長さ:\n\n`len(seq)`\n\n内部: `seq.__len__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "シーケンスが特定の値を含んでいるかどうかを知る:\n\n`x in seq`\n\n内部: `seq.__contains__(x)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`cgi`モジュールは、これらのメソッドを `FieldStorage`クラスの中で使っている。\nこのクラスは、動的なWebページに送信されたすべてのフォームフィールドやクエリクエリパラメータを表現するもの。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import cgi\ncgi", | |
"execution_count": 13, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "<module 'cgi' from 'C:\\\\Miniconda3\\\\lib\\\\cgi.py'>" | |
}, | |
"metadata": {}, | |
"execution_count": 13 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`cgi.FieldStorage`クラスのインスタンスを作成すると、\nクエリ文字列に特定のパラメータが含まれているかどうかを `in`演算子を使って確認できる。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "fs = cgi.FieldStorage()\nif 'q' in fs:\n print('contain \"q\"')", | |
"execution_count": 14, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "```\n# 動作を説明するために、cgi.pyから抜粋\nclass FieldStorage:\n.\n.\n.\n def __contains__(self, key): 2\n if self.list is None:\n raise TypeError('not indexable')\n return any(item.name == key for item in self.list) 3\n\n def __len__(self): 4\n return len(self.keys()) 5\n```\n\n2.`__contains__()`メソッド。\n\n3.`if 'q' in fs` と書くと、Pythonは `__contains__()`メソッドを `fs`オブジェクトの中から探し出す。\nこのメソッドは `cgi.py` に定義されている。\n`'q'` の値は引数keyとして `__contains__()`メソッドに渡される。\n\n4.この`FieldStorage`クラスは長さを返す機能もサポートしているので、`len(fs)` と書くことができる。\nこれは`FieldStorage`クラスの `__len__()`メソッドを呼び出し、このクラスが認識したクエリパラメータの数が返される。\n\n5.`self.list is None` であるかどうかは `self.keys()`メソッドが確認するので、`__len__`メソッドが余計なエラーチェックをする必要はない。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 辞書のように振る舞うクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "前節の内容を少し拡張することで、`in`演算子 と `len()`関数に応答するだけでなく、\nキーに基づいて値を返す完全な辞書として振る舞うクラスを定義できる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "キーに対応する値を得る:\n\n`x[key]`\n\n内部: `x.__getitem__(key)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "キーに対応する値を設定する:\n\n`x[key] = value`\n\n内部: `x.__setitem__(key, value)`\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "キーと値のペアを削除する:\n\n`del x[key]`\n\n内部: `x.__delitem__(key)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "存在しないキーのためのデフォルト値を提供する:\n\n`x[nonexistent_key]`\n\n内部: `x.__missing__(nonexistent_key)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "cgiモジュールにあるFieldStorageクラスもこれらの特殊メソッドを実装しており、これは次のようなことができることを意味している:" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`fs`オブジェクトは `cgi.FieldStorage` のインスタンスだが、`fs['q']` のような式を評価することもできる。" | |
}, | |
{ | |
"metadata": { | |
"collapsed": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "# A script which responds to http://example.com/search?q=cgi\nimport cgi\nfs = cgi.FieldStorage()\nif 'q' in fs:\n print('q= ' + fs['q']) # 1", | |
"execution_count": 15, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`fs['q']` は、key引数に'q'を設定して `__getitem__()`メソッドを呼び出す。\nこのメソッドは、内部に保持されたクエリパラメータのリスト (`self.list`) の中から、\nその `.name` が与えられたキーと一致する要素を探し出す。\n\n```\n# 動作を示すために、cgi.pyから抜粋\nclass FieldStorage:\n.\n.\n.\n def __getitem__(self, key): # 2\n if self.list is None:\n raise TypeError('not indexable')\n \n found = []\n for item in self.list:\n if item.name == key: found.append(item)\n \n if not found:\n raise KeyError(key)\n \n if len(found) == 1:\n return found[0]\n else:\n return found\n```" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 数値のように振る舞うクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "適切な特殊メソッドを使うと、数値のように振る舞う独自のクラスを定義できる。\n要するに、それらを足したり、引いたり、その他の数学的な演算を行うことができるようになる。\n`fractions`モジュールはこの方法で実装されている \n\n— つまり `Fraction`クラスはこれらの特殊メソッドを実装しており、それによって次のようなことができるようになっている:" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "from fractions import Fraction\nx = Fraction(1, 3)\nx / 3", | |
"execution_count": 16, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "Fraction(1, 9)" | |
}, | |
"metadata": {}, | |
"execution_count": 16 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "数値のように振る舞うクラスを実装する際に必要となる特殊メソッドの包括的なリストを示す。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "|演算|実際のコード|内部で呼び出さるメソッド|\n|:--:|:--:|:--:|\n| 加算 | `x + y` | `x.__add__(y)` |\n| 減算 | `x - y` | `x.__sub__(y)` |\n|乗算\t |`x * y` | `x.__mul__(y)` |\n|除算\t |`x / y`| `x.__truediv__(y)` |\n|整数除算 |`x // y`|`x.__floordiv__(y)`|\n|モジュロ (余り)|` x % y `|` x.__mod__(y) `|\n|整数除算とモジュロ |`divmod(x, y) `|` x.__divmod__(y)`|\n|べき乗|` x ** y `|` x.__pow__(y) `|\n|左ビットシフト|` x << y `|` x.__lshift__(y) `|\n|右ビットシフト|` x >> y `|` x.__rshift__(y)`|\n|ビット単位のand|`x & y `|`x.__and__(y) `|\n|ビット単位のxor|`x ^ y `|`x.__xor__(y) `|\n|ビット単位のor |x | y|` x.__or__(y) `|" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "これらを実装しているのに、ある種の引数を扱うことができない場合はどうなるのだろうか? 例えばこういうことだ:" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"scrolled": true, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "from fractions import Fraction\nx = Fraction(1, 3)\n1 / x", | |
"execution_count": 17, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"data": { | |
"text/plain": "Fraction(3, 1)" | |
}, | |
"metadata": {}, | |
"execution_count": 17 | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "この例は(前の例のように)`Fraction` を整数で割っているのではない。\n前の例は分かりやすかった。\n前の例は、`x / 3` が `x.__truediv__(3)` を呼び出し、\n`Fraction` の `__truediv__()`メソッドがすべての計算を処理していた。\nしかし整数型は分数を使った算術演算のやり方を「知らない」。\nだとすると、この例はどのように動作しているのだろうか?" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "算術演算の特殊メソッドには、\n演算対象を反転させた2つ目のセットが存在する。\n被演算子を2つとる算術演算(例えば `x / y` )が与えられた場合、\nこれを実行する方法は2通り存在する:" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "- オブジェクト`x` に対して、自身を`y` で割るように告げる、もしくは(x/y)\n- オブジェクト`y` に対して、`x`を自身で割るように告げる。(x/y)" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "先に挙げた特殊メソッドのセットは1つ目のアプローチを取る。\n\nつまりこれらの特殊メソッドは、x / yが与えられたときに`x` が「私は自分を`y` で割る方法を知っているよ」と言うための手段を提供する。\n\n以下に挙げる特殊メソッドの集合は2つ目のアプローチを取る。\nつまりこれらの特殊メソッドは、`y` が「私は、自分が分母になってxを割る方法を知っている」と言うための手段を提供する。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "| 演算 | 実際のコード | 内部で呼び出さるメソッド |\n|:--:|:--:|:--:|\n| 加算 | `x + y` | `y.__radd__(x)` |\n| 減算 | `x - y` | `y.__rsub__(x)` |\n|乗算\t | `x * y` | `y.__rmul__(x)` |\n|除算\t | `x / y` | `y.__rtruediv__(x)` |\n|整数除算 | `x // y` | `y.__rfloordiv__(x)`|\n|モジュロ (余り)| ` x % y `|` y.__rmod__(x) `|\n|整数除算とモジュロ | `divmod(x, y) `|` y.__rdivmod__(x)`|\n|べき乗| ` x ** y ` | ` y.__rpow__(x) `|\n|左ビットシフト| ` x << y ` | ` y.__rlshift__(x) ` |\n|右ビットシフト| ` x >> y ` | ` y.__rrshift__(x)` |\n|ビット単位のand| `x & y ` | `y.__rand__(x) ` |\n|ビット単位のxor| `x ^ y ` | `y.__rxor__(x) ` |\n|ビット単位のor | x | y |` y.__ror__(x) ` |" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`x /= 3` のような「インプレイス」の演算を行おうとする場合は、定義可能な特殊メソッドがさらに存在する。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "|演算|実際のコード|内部で呼び出さるメソッド|\n|:--:|:--:|:--:|\n|インプレイスの加算 | `x += y` | `x.__iadd__(y)` |\n| インプレイスの減算 | `x -= y` | `x.__isub__(y)` |\n|インプレイスの乗算\t |`x *= y` | `x.__imul__(y)` |\n|インプレイスの除算\t |`x /= y`| `x.__itruediv__(y)` |\n|インプレイスの整数除算 |`x //= y`|`x.__ifloordiv__(y)`|\n|インプレイスのモジュロ (余り)|` x %= y `|` x.__imod__(y) `|\n|インプレイスのべき乗|` x **= y `|` x.__ipow__(y) `|\n|インプレイスの左ビットシフト|` x <<= y `|` x.__ilshift__(y) `|\n|インプレイスの右ビットシフト|` x >>= y `|` x.__irshift__(y)`|\n|インプレイスのビット単位のand|`x &= y `|`x.__iand__(y) `|\n|インプレイスのビット単位のxor|`x ^= y `|`x.__ixor__(y) `|\n|インプレイスのビット単位のor |x |= y|` x.__ior__(y) `|" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "注: ほとんどの状況において、インプレイス演算のメソッドは必要ない。\n特定の演算用のインプレイスメソッドが定義されていない場合、\nPython は別の方法でそれらのメソッドを試みようとする。\n\n例えば、`x /= y` を実行するとき、Pythonは次のように動作する:\n\n1. `x.__itruediv__(y)` の呼び出しを試みる。\nこのメソッドが定義されていて `NotImplemented`以外の値を返すのであれば、完了。\n\n2. `x.__truediv__(y)` の呼び出しを試みる。\nこのこのメソッドが定義されていて `NotImplemented`以外の文字を返すのであれば、\nxの古い値を捨てて、戻り値で置き換える。\nつまり、代わりに `x = x / y` を実行したのとちょうど同じこと。\n\n3. `y.__rtruediv__(x)` の呼び出しを試みる。\nこのメソッドが定義されていて `NotImplemented`以外の文字を返すのであれば、`x` の古い値を捨てて、戻り値で置き換える。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "したがって、`__itruediv__()` のようなインプレイス演算のメソッドを定義しなければならないのは、\nインプレイス演算子に対して何か特別な最適化を施したい場合だけ。\n\nそうでなければ、原則的にPythonは、\n通常の+演算子と変数代入を組合せてインプレイス演算の式を作り上げる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "「単項」演算もいくつか存在し、\nこれらは、数値としてふるまうオブジェクト1つに対して実行できる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "|演算|実際のコード|内部で呼び出さるメソッド|\n|:--:|:--:|:--:|\n|負の数|`-x`|`x.__neg__()`|\n|正の数|`+x`|`x.__pos__()`|\n|絶対値|`abs(x)`|`x.__abs__()`|\n|反転|`~x`|`x.__invert__()`|\n|複素数|`complex(x)`|`x.__complex__()`|\n|整数|`int(x)`|`x.__int__()`|\n|浮動小数点数|`float(x)`|`x.__float__()`|\n|最も近い整数へ丸めた数値|`round(x)`|`x.__round__()`|\n|最も近いn桁へ丸めた数値|`round(x, n)`|`x.__round__(n)`|\n|最小の整数 >= x|`math.ceil(x)`|`x.__ceil__()`|\n|最大の整数 <= x|`math.floor(x)`|`x.__floor__()`|\n|0に向けた最も近い整数への切り捨て|`math.trunc(x)`|`x.__trunc__()`|\n|PEP 357 リストのインデックスとしての数|`a_list[x]`|`a_list[x.__index__()]`|" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 比較可能なクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "多くのデータ型は比較できる — 文字列、リスト、そして辞書でさえも比較可能。\nクラスを作る際に、そのオブジェクト同士の比較が意味をなすのであれば、\n以下の特殊メソッドを使って比較を実装できる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "|演算|実際のコード|内部で呼び出さるメソッド|\n|:--:|:--:|:--:|\n|等式|`x == y`|`x.__eq__(y)`|\n|不等|`x != y`|`x.__ne__(y)`|\n|より小さい|`x < y`|`x.__lt__(y)`|\n|より小さいか等しい|`x <= y`|`x.__le__(y)`|\n|より大きい|`x > y`|`x.__gt__(y)`|\n|より大きいか等しい|`x >= y`|`x.__ge__(y)`|\n|ブール値のコンテクストでの真偽値|`if x:`|`x.__bool__()`|" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__lt__()`メソッドが定義されているが `__gt__()`メソッドは定義されていない場合、\nPythonは被演算子の順番を入れ替えて `__lt__()`メソッドを使用する。\n\nしかし、Pythonが演算子を組み合わせることはない。\n例えば、`__lt__()`メソッドと `__eq()__` を実装して`x <= y` が成り立つかを確認しようとしても、\nPythonが `__lt__()` と `__eq()__` を順に呼び出すことはない。\nPythonは `__le__()`メソッドしか呼び出さない。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## シリアライズ可能なクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Pythonは、任意のオブジェクトのシリアライズとアンシリアライズをサポートしている\n(Pythonの文献の多くは、これを「Pickle化」や「非Pickle化」と呼んでいる)。\nこの機能は、ファイルに状態を保存して後でそれを復元するのに役立つ。\nすべてのネイティブデータ型はあらかじめPickle化をサポートしている。\n\n作成したクラスを `Pickle`化に対応させたい場合は、`Pickle`プロトコルに関する説明を読み、\n下記の特殊メソッドがいつ・どのように呼び出されるのかを確認。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "オブジェクトのカスタマイズされたコピー:\n\n`copy.copy(x)`\n\n内部:`x.__copy__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "オブジェクトのカスタマイズされた深いコピー:\n\n`copy.deepcopy(x)`\n\n内部: `x.__deepcopy__()`\n" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Pickle化する前のオブジェクトの状態の取得:\n\n`pickle.dump(x, file)`\n\n内部: `x.__getstate__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "オブジェクトのシリアライズ:\n\n`pickle.dump(x, file)`\n\n内部: `x.__reduce__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "オブジェクトのシリアライズ(新Pickle化プロトコル):\n\n`pickle.dump(x, file, protocol_version)`\n\n内部: `x.__reduce_ex__(protocol_version)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "非Pickle化時にどのようにオブジェクトを生成するかの制御\t\n\n`x = pickle.load(file)`\t\n\n内部: `x.__getnewargs__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "非Pickle化の後にオブジェクトの状態を復元する:\n\n`x = pickle.load(file)`\n\n内部: `x.__setstate__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "シリアライズされたオブジェクトを再作成するには、\nPythonは、シリアライズされたオブジェクトに似せた新しいオブジェクトを作り、\nその新しいオブジェクトにすべての属性値を設定しなければならない。\n\n`__getnewargs__()`メソッドは、オブジェクトがどのように作られるのかを制御し、\n`__setstate__()`メソッドは属性値がどのように設定されるのかを制御する。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## withブロックで使えるクラス" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`with`ブロックは実行時コンテクストを定義する。\n`with`文を実行するとそのコンテクストに「入り」、\nそのブロックの最後の文を実行し終えるとそのコンテクストから「出る」。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "withブロックに入るときに何か特別なことをする:\n\n`with x:`\n\n内部: `x.__enter__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "withブロックから出るときに何か特別なことをする:\n\n`with x:`\n\n内部: `x.__exit__(exc_type、exc_value、traceback)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "これは、with fileイディオムがどのように動作するのかを示している。\n\n```\n# excerpt from io.py:\n\ndef _checkClosed(self, msg=None):\n '''Internal: raise an ValueError if file is closed\n '''\n if self.closed:\n raise ValueError('I/O operation on closed file.'\n if msg is None else msg)\n\ndef __enter__(self):\n '''Context management protocol. Returns self.'''\n self._checkClosed() 1\n return self 2\n\ndef __exit__(self, *args):\n '''Context management protocol. Calls close()'''\n self.close() 3\n```\n\n1. ファイルオブジェクトは `__enter__()`メソッドと `__exit__()`メソッドを定義している。`__enter__()`メソッドはファイルが開かれていることを確認する。もしそうでない場合は`_checkClosed()`メソッドが例外を発生する。\n\n2. `__enter__()`メソッドは、ほぼすべての場合において `self` を返すべき。\nこのオブジェクトは、withブロックがプロパティやメソッドをディスパッチするために使用する。\n\n3. `with`ブロックが終わると、\nファイルオブジェクトは自動的に閉じられる。\n`__exit__()`メソッドの中で、`self.close()` を呼び出している。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "`__exit__()`メソッドは、\nたとえ `with`ブロックの中で例外が発生したとしても必ず呼び出される。\n事実、例外が発生した場合は、例外の情報が `__exit__()`メソッドに渡される。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 完全に奥義的なもの" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "何をしているのかを理解しているのであれば、クラスがどのように比較されるか、属性がどのように定義されるか、どんなクラスがそのクラスのサブクラスと見なされるか、などの制御をほぼ完全に掌握できる。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスのコンストラクタ:\n\n`x = MyClass()`\n\n内部: `x.__new__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスのデストラクタ:\n\n`del x`\n\n内部: `x.__del__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "指定した属性のみを定義できるようにする:\n\n内部: `x.__slots__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "カスタムのハッシュ値:\n\n`hash(x)`\n\n内部: `x.__hash__()`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "プロパティの値を取得する:\n\n`x.color`\n\n内部: `type(x).__dict__['color'].__get__(x, type(x))`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "プロパティの値を設定する:\n\n`x.color = 'PapayaWhip'`\n\n内部: `type(x).__dict__['color'].__set__(x, 'PapayaWhip')`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "プロパティを削除する:\n\n`del x.color`\n\n内部: `type(x).__dict__['color'].__delete__(x)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "オブジェクトがクラスのインスタンスかどうかの判断を制御する:\n\n`isinstance(x, MyClass)`\n\n内部: `MyClass.__instancecheck__(x)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスが自作クラスのサブクラスかどうかの判断を制御する:\n\n`issubclass(C, MyClass)`\n\n内部: `MyClass.__subclasscheck__(C)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "クラスが自作の[抽象基底クラス](http://docs.python.jp/3.5/library/abc.html)のサブクラスかどうかの判断を制御する:\n\n`issubclass(C, MyABC)`\n\n内部: `MyABC.__subclasshook__(C)`" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": " Pythonが特殊メソッド `__del__()` を呼び出す正確なタイミングは信じられないほど複雑。これを完全に理解するには、Python がメモリ上でオブジェクトを追跡する方法を知る必要がある。" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "## 参考リンク" | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "- [Dive Into Python3 AppendixB 特殊メソッド名](http://diveintopython3-ja.rdy.jp/special-method-names.html#acts-like-dict)\n- [abc — 抽象基底クラス(原文)](http://docs.python.jp/3.5/library/abc.html)" | |
}, | |
{ | |
"metadata": { | |
"collapsed": false, | |
"scrolled": false, | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def str2table(st:str):\n import re\n dist = re.sub(r'^([^|])', r'|\\1', st, flags=re.MULTILINE)\n dist = re.sub(r'([^|])$', r'\\1|', dist, flags=re.MULTILINE)\n dist = re.sub(r'\\t[\\t|\\b]*', r'|', dist)\n print(dist)\n\nst = '''等式\tx == y\tx.__eq__(y)\n不等\tx != y\tx.__ne__(y)\nより小さい\tx < y\tx.__lt__(y)\nより小さいか等しい\tx <= y\tx.__le__(y)\nより大きい\tx > y\tx.__gt__(y)\nより大きいか等しい\tx >= y\tx.__ge__(y)\nブール値のコンテクストでの真偽値\tif x:\tx.__bool__()'''\nprint(str2table(st))", | |
"execution_count": null, | |
"outputs": [] | |
} | |
], | |
"metadata": { | |
"_draft": { | |
"nbviewer_url": "https://gist.github.com/182f6bd4e59d4a399134336fdbdce82b" | |
}, | |
"gist": { | |
"id": "182f6bd4e59d4a399134336fdbdce82b", | |
"data": { | |
"description": "Dive Into Python3 AppnedixBメモ(特殊メソッド名)", | |
"public": true | |
} | |
}, | |
"language_info": { | |
"name": "python", | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"pygments_lexer": "ipython3", | |
"nbconvert_exporter": "python", | |
"mimetype": "text/x-python", | |
"version": "3.5.1" | |
}, | |
"toc": { | |
"toc_number_sections": true, | |
"toc_threshold": "6", | |
"toc_cell": true, | |
"toc_window_display": false | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3", | |
"language": "python" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment