まとめへのリンク
はじめに
Pythonの勉強をするために、acopyという群知能ライブラリを写経していました。
acopyでは、多くのPythonの面白い文法・イディオムが使われており、その中でも便利だなぁというのをまとめています。
今回は、クラスに__getattr__
特殊メソッドを定義してみます。
インスタンス属性
Pythonでインスタンス内部で使用される属性を取り出す方法として
__dict__
を参照する方法があります。
インスタンス内部で使用される名前空間の名前が__dict__
に辞書として定義されています。
例えば、以下のような例を見てみます。
class A:
def __init__(self, x, y):
self.x = x
self.y = y
# {'x': 10, 'y': 20}
a = A(10, 20)
print(a.__dict__)
aというインスタンスには、$x, y$の名前が存在し
それらを辞書として取り出すことができます。
__getattr__
Pythonのクラスには、__getattr__
という特殊メソッドを定義することができます。
Pythonのインスタンスの名前呼び出しにおいて
もし定義されていない名前で呼ばれればAttributeErrorを返します。
しかし、もし__getattr__
が定義されている場合は、
「名前が定義されていない呼び出し時に」AttributeErrorを返す代わりに、__getattr__
が実行されます。
例を見てみます。
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def __getattr__(self, name):
print(name, 'getattr')
if name == 'hello':
return self.world
raise AttributeError
def world(self, x):
return x
'''
{'x': 10, 'y': 20}
hello getattr
<bound method A.world of <__main__.A object at 0x10e602150>>
hello getattr
20
'''
a = A(10, 20)
print(a.__dict__)
print(a.hello)
print(a.hello(20))
上のAクラスには__getattr__
メソッドが定義されています。
a.hello
を呼び出そうとすると、helloという属性がaに定義されていません。
そのため、__getattr__
メソッドが呼び出され、代わりにworldメソッドを返しています。
a.hello(20)
では、まずworldメソッドが返されて、worldメソッドに20という値を与えて実行しています。
よって、helloという名前が確定した時点で実行されています。
よって、以上の性質を使うと、以下のような無駄コードをかけます。
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def __getattr__(self, name):
print(name, 'getattr')
if name == 'hello':
return self.world
if name == 'world':
return self.great
if name == 'great':
return self.bad
if name == 'bad':
return 0
raise AttributeError
'''
{'x': 10, 'y': 20}
hello getattr
world getattr
great getattr
bad getattr
0
'''
a = A(10, 20)
print(a.__dict__)
print(a.hello)
応用例
あまり応用例は思いつきませんが、
もし属性がない場合に代わりの規程処理をやらせる、などがあるのかなと思います。
また、分岐などを呼び出し名によって変えるなどがありそうです。
class A:
def __getattr__(self, name):
self.nothing(name)
def exist(self):
print('exist!')
def nothing(self, name):
print(f'nothing {name}')
'''
exist!
nothing unnti
'''
a = A()
a.exist()
a.unnti
また、後日別記事として各内容として
lambda式で名前がないとき例外コードを投げるなどでも使えそうです。