1.はじめに
Pythonでよくオブジェクト定義に用いられたり,いろんなオブジェクトに元からくっついてる__
で囲まれた__hoge__
や__hoge__()
をそれぞれ特殊属性(特殊アトリビュート),特殊メソッドと言います.
これを使いこなしている人を見るとPython中級者以上感がするので僕も使いこなせるようになろうというメモです.
この特殊属性や特殊メソッドはオブジェクト指向の書き方です.Pythonのオブジェクト指向プログラミング(Object Oriented Programming)をOOPyと勝手に呼んでいるので是非使ってください.読み方は各自に任せます.
特殊属性や特殊メソッドは割といっぱいあるのでこの記事に順次追加していく予定です.
2.環境
- Python 3.6.0
3.特殊属性
__dict__
そのオブジェクトのアトリビュート(変数とか配列とかメソッドではないもの)を辞書にして返す.
class DictTest(object):
def __init__(self):
self.a = 1
self.b = [1,2]
def c(self):
pass
dicttest = DictTest()
print(dicttest.__dict__)
# {'a': 1, 'b': [1, 2]}
上のようにaとbのアトリビュートだけ格納されてます.cのメソッドについては格納されていません.
ちなみにpythonチュートリアルによると,これを使うのは検死型のデバッガなどに限るべきだそうです.
これは,例えばself.a
にアクセスするときは何か処理をしてから返すというようになっていた場合も,それを全部無視してアクセスできてしまうためだと思われます.
__doc__
def
定義やclass
定義宣言の直下に複数行コメントがあるとそれを読み込んで,ここに格納する.
help()
によってこの情報を表示してくれる.
ちなみにサブクラスには継承されない.
def doc_test_func1():
"""This function is ...
hogehoge
"""
pass
def doc_test_func2():
pass
print(doc_test_func1.__doc__)
# This function is ...
# hogehoge
help(doc_test_func1)
print(doc_test_func2.__doc__)
# None
# これにも__doc__アトリビュートはついてるが中身がNoneになっている.
help(doc_test_func2)
__annotations__
関数注釈をすると,この__annotations__
に情報が保存される.
ちなみに関数注釈とは,引数や返り値がどんなデータ型か人間用に見やすく表記する書き方である.
あくまでも人間用で,その注釈と違ったことをしてもエラーが出たりするわけではない.
詳細はPythonではじまる、型のある世界などを参照.
# 関数注釈
def ann_test_func(param1: str, param2: int=1) -> str:
output = param1 + str(param2)
print(output)
return output
print(ann_test_func.__annotations__)
# {'param1': <class 'str'>, 'param2': <class 'int'>, 'return': <class 'str'>}
# このように各引数や,返り値がどんなデータ型か辞書で保存されている.
ann_test_func("test", 1)
# test1
ann_test_func("test", "a")
# testa
# intの場所にstrを入れても怒られない.
__module__
関数が定義されているモジュールの名前を返してくれます.
class ModuleTest(object):
pass
modeletest = ModuleTest()
ModuleTest.__module__
# __main__
pythonでは,対話型環境もひとつのモジュールで,その名前は__main__
と名付けられているためこのように表示されます.
4.特殊メソッド
__init__
オブジェクト指向で言われるところのコンストラクタ.
(下記コメントにあるようにイニシャライザと呼んだほうがいいようだ.Pythonには正確にこれがコンストラクタと呼べるものはないらしい.__new__と__init__とメタクラスとなどを参照.)
インスタンス化するときに呼ばれる.
class InitTest(object):
def __init__(self):
print("URYYYYYYYYYY")
inittest = InitTest() # ここで呼ばれる
# URYYYYYYYYYY
__iter__, __next__
__iter__
は,pythonドキュメントによるとイテレーターオブジェクトを返すものとある.つまり,元々イテレーターでないものを変換するといった処理を書くメソッドである.
class IterTest(object):
def __init__(self, *nums):
self.nums = nums
def __iter__(self):
return iter(self.nums)
itertest = IterTest(1,2,3)
for i in itertest:
print(i)
# 1
# 2
# 3
__next__
は,次の値を渡す部分を書き,もう渡すものがなくなったらStopIteration例外を出すように実装する.
詳細はPythonのイテレータとジェネレータを参照.この記事のサンプルコードを引っ張ってくると以下のような感じ.
class MyIterator(object):
def __init__(self, *numbers):
self._numbers = numbers
self._i = 0
def __iter__(self):
return self
def __next__(self):
# なぜか元の記事ではメソッド名がnextだが,__next__にすると動いた.
if self._i == len(self._numbers):
raise StopIteration()
value = self._numbers[self._i]
self._i += 1
return value
__del__
このメソッドはデストラクタとも呼ばれる.インスタンスが消滅させられるときに呼ばれる.
class DelTest(object):
def __del__(self):
print("俺はまだ死にたくない!!")
deltest = DelTest()
del deltest
# 俺はまだ死にたくない!!
__call__
オブジェクト自体をメソッドのように書いたとき,この__call__
を実行する.
class CallTest(object):
def __call__(self):
print("呼んだか?")
calltest = CallTest()
calltest()
# 呼んだか?
__setattr__,__getattr__, __delattr__
__setattr__
はアトリビュートに値を代入するときに呼ばれる.
__getattribute__
はアトリビュートがあろうがなかろうが呼ばれる.親クラスの__getattribute__
を使うとかしないとすぐに再帰的エラーになる.(__getattribute__
の中で__getattribute__
呼んで...が繰り返される.)
__getattr__
はアトリビュートにアクセスするときに,そのアトリビュートがなかったときに呼ばれる.
class SetGetDelattrTest(object):
def __setattr__(self, name, value):
self.__dict__[name] = value
print(f"{name} : {value} has been set!")
def __getattribute__(self, name):
print("URYYYYYY")
return super().__getattribute__(name)
def __getattr__(self, name):
print(f"try to return value of {name}")
raise AttributeError("nothing...")
def __delattr__(self, name):
print("not work!")
sgdattr = SetGetDelattrTest()
sgdattr.a = 1 # __setattr__
sgdattr.a # __getattribute__
sgdattr.b # __getattr__
del sgdattr.a # __delattr__
# not work!
sgdattr.a
# URYYYYYY
# 1
# __delattr__に消すコードを書いていないので生きてる.
__setattr__
とかの中のself.
でも呼ばれているらしくURYYYYYYURYYYYYYめっちゃうるさい.
これはアトリビュートへのアクセスの仕方全てに関する特殊メソッドだが,ぶっちゃけこの辺はあまり使わないと思われる.
実用上はあるアトリビュートに対して個別にsetとgetをいじりたい場合が多い.そんなときは次の@property
デコレータを使うと便利です.
@property
デコレータは,ある関数に対して,この関数はこういうものだという追加の設定ができるようなものです.
デコレータについてはPythonのデコレータについてあたり.
特に,@property
に関してはPython @propertyが良い感じです.
まず@property
を使って,アトリビュートと同じ名前のメソッドを定義します.これがgetterになります.
次に同じ名前で@x.setter
,@x.deleter
デコレータを使って,値代入時に呼ばれるsetterや削除時に呼ばれるdeleterを定義していきます.(なくても良い)
注意点としては,オブジェクト内ではプライベート変数として_x
を定義しておいて,対人間用としてアトリビュートxでsetしたりgetするようにすることです.
そうしないと,getterのところで,object.x
でxの値を取りにいったときに,中のreturn self.x
でまたgetterを呼び出して...と延々とループします.
class PropertyTest(object):
def __init__(self, x):
self._x = x
self.y = 1
@property
def x(self):
print("getting x")
return self._x
@x.setter
def x(self, x):
if not isinstance(x, float):
raise ValueError("hoge")
print("setting x")
self._x = x
@x.deleter
def x(self):
print("頼む!殺さないでくれ!")
del self._x
proptest = PropertyTest(x=10.)
proptest.x = 20.
# setting x
print(proptest.x)
# getting x
# 20.0
del proptest.x
# 頼む!殺さないでくれ!
print(proptest.y)
# 1
# アトリビュートyについては特に何も変更されていない.
アトリビュートyのgetterには特に何の変更もされておらず,xについてだけ修正されていることがわかります.
__dir__
インスタンスに対してdir()
をすると動くメソッド.
デフォルトから変更すると嬉しい理由が特に思いつかない.
class DirTest(object):
def __dir__(self):
print("dirdirdir")
return [1,2,3]
dirtest = DirTest()
dir(dirtest)
# dirdirdir
# [1, 2, 3]
5.reference
- https://docs.python.jp/3.5/reference/datamodel.html
- https://stackoverflow.com/questions/3278077/difference-between-getattr-vs-getattribute
6.おわりに
もっとOOPy触って育てたい.