単語帳=毎回検索するのが面倒なので転載多め.元URLあり.
主に自作クラス関連.
クラスの定義
抽象クラス
テンプレート
from abc import ABCMeta, abstractmethod
# 抽象クラス
class AbstClass(metaclass=ABCMeta):
@abstractmethod
def my_abstract_method(self):
pass
@property
@abstractmethod
def my_abstract_property(self):
pass
[Qiita@kaneshin: PythonのABC - 抽象クラスとダック・タイピング]
(https://qiita.com/kaneshin/items/269bc5f156d86f8a91c4)
[stackoverflow: How to create abstract properties in python abstract classes]
(https://stackoverflow.com/questions/5960337/how-to-create-abstract-properties-in-python-abstract-classes)
※親クラス (含抽象クラス) ではメソッドの引数の個数などを拘束できない
(オーバーライドすると,サブクラスでの定義が優先される)
from abc import ABCMeta, abstractmethod
class AbstClass(metaclass=ABCMeta):
@abstractmethod
def mymethod(self, arg1, arg2):
pass
class SuperClass:
def mymethod(self, arg1, arg2):
pass
class MyClass(AbstClass):
# class MyClass(SuperClass): 同じく拘束できない
def __init__(self):
pass
def mymethod(self, arg1):
print(arg1)
obj = MyClass()
obj.mymethod(1)
obj.mymethod(1, 2) # TypeError: mymethod() takes 2 positional arguments but 3 were given
[stackoverflow: Abstract classes with varying amounts of parameters]
(https://stackoverflow.com/questions/42778784/abstract-classes-with-varying-amounts-of-parameters)
@abstractmethod
の'__' (アンダースコア2つ) で始まるメソッドをオーバーライドするには
通常の方法ではオーバーライドできない.
(抽象クラスの継承時にこの問題が起きるとCan't instantiate abstract class … with abstract methods
のエラー.)
_親クラス名__メソッド名
でオーバーライド.
@abstractmethod
を使わない場合,この問題は起こらない:
class BaseClass0:
def __init__(self): pass
def mymethod0(self):
print('BaseClass0.mymethod0')
def _mymethod1(self):
print('BaseClass0._mymethod1')
def __mymethod2(self):
print('BaseClass0.__mymethod2')
def mymethod2(self):
print('BaseClass0.mymethod2')
self.__mymethod2()
class BaseClass1(metaclass=ABCMeta):
def __init__(self): pass
def mymethod0(self):
print('BaseClass1.mymethod0')
def _mymethod1(self):
print('BaseClass1._mymethod1')
def __mymethod2(self):
print('BaseClass1.__mymethod2')
def mymethod2(self):
print('BaseClass1.mymethod2')
self.__mymethod2()
class SubClass0(BaseClass0):
def __init__(self): pass
def mymethod0(self):
print('SubClass0.mymethod0')
def _mymethod1(self):
print('SubClass0._mymethod1')
def __mymethod2(self):
print('SubClass0.__mymethod2')
def mymethod2(self):
print('SubClass0.mymethod2')
self.__mymethod2()
class SubClass1(BaseClass1):
def __init__(self): pass
def mymethod0(self):
print('SubClass1.mymethod0')
def _mymethod1(self):
print('SubClass1._mymethod1')
def __mymethod2(self):
print('SubClass1.__mymethod2')
def mymethod2(self):
print('SubClass1.mymethod2')
self.__mymethod2()
for obj in [BaseClass0(), SubClass0(), BaseClass1(), SubClass1()]:
obj.mymethod0()
obj._mymethod1()
obj.mymethod2()
しかし,親クラスに@abstractmethod
をつけた途端,インスタンスの生成も出来なくなる:
class BaseClass2(metaclass=ABCMeta):
@abstractmethod
def mymethod0(self): pass
@abstractmethod
def _mymethod1(self): pass
@abstractmethod
def __mymethod2(self): pass
@abstractmethod
def mymethod2(self): pass
class SubClass2(BaseClass2):
def __init__(self): pass
def mymethod0(self):
print('SubClass2.mymethod0')
def _mymethod1(self):
print('SubClass2._mymethod1')
def __mymethod2(self):
print('SubClass2.__mymethod2')
def mymethod2(self):
print('SubClass2.mymethod2')
self.__mymethod2()
obj = SubClass2() # TypeError: Can't instantiate abstract class SubClass2 with abstract methods _BaseClass2__mymethod2
これを解決するためには,
-
__mymethod2
の代わりに_BaseClass2__mymethod2
をオーバーライド -
self.__mymethod2()
では呼び出しが出来ないので,これをself._BaseClass2__mymethod2()
に変更
の2点を行う:
class SubClass2_ver2(BaseClass2):
def __init__(self): pass
def mymethod0(self):
print('SubClass2.mymethod0')
def _mymethod1(self):
print('SubClass2._mymethod1')
def _BaseClass2__mymethod2(self):
print('SubClass2.__mymethod2')
def mymethod2(self):
print('SubClass2.mymethod2')
self._BaseClass2__mymethod2()
# self.__mymethod2() AttributeError: 'SubClass2_ver2' object has no attribute '_SubClass2_ver2__mymethod2'
obj = SubClass2_ver2()
obj.mymethod0()
obj._mymethod1()
obj.mymethod2()
[stackoverflow: Can't instantiate abstract class … with abstract methods]
(https://stackoverflow.com/a/31458576)
class MyClass(object):
の(object)
は省略可能 (Python3のみ)
元々クラスには2種類 (Python2.1以前と以降) の2種類があり,
Python2だと(object)
を
- 明記: 新スタイル
- 省略: 旧スタイル,
@property
のsetterが動かなかったり
として区別される.Python3では新スタイルに統一されている.
[Qiita@alt-core: Python2.7で class C: と class C(object): の違いに大ハマりした話]
(https://qiita.com/alt-core/items/d20e9a1864ff3a35946e)
クラスのキャスト
[民主主義に乾杯: クラスをキャストする]
(https://python.ms/cast/)
メソッド
特殊メソッド
一覧:
[公式: 3. Data model > 3.3. Special method names]
(https://docs.python.org/3/reference/datamodel.html#special-method-names)
__contains(self, item)__
: item in self
[stackoverflow: Override Python's 'in' operator?]
(https://stackoverflow.com/questions/2217001/override-pythons-in-operator)
__getattr__
と__getattribute__
-
__getattr__
: 未定義のメンバーにアクセスするとき- 明示的に定義していない特殊メソッドにアクセスしたときも
-
__getattribute__
: 未定義・定義済み関わらず、すべてのメンバーアクセスで
[SDN開発エンジニアを目指した活動ブログ: Python勉強メモ:特殊メソッド__getattribute__とは?]
(http://ttsubo.hatenablog.com/entry/2014/05/04/215202)
[Qiita@fumitoh: ドットによるオブジェクトの属性の取得をカスタマイズするための__getattr__と__getattribute__]
(https://qiita.com/fumitoh/items/b090ae6ccd52e6a88d9e)
__call__
の使い方
[Qiita@ganariya: 【Python中級者への道】__call__でクラスインスタンスを関数のように呼び出す]
(https://qiita.com/ganariya/items/27ade6b149728f6aae88)
__getattr__
の副作用
1つ1つ属性やゲッターを定義しなくても良くなる半面,副作用もある:
- pickle化できなくなる
-
copy.copy
やcopy.deepcopy
が動作しなくなる
解決策としては,
-
__getattr__
内で特殊メソッドを除外する (色々な問題に効果あり) - 各問題に対して処方箋
pickle化できなくなる例
pickle化できるかどうかの確認方法
import pickle
def is_picklable(obj):
try:
pickle.dumps(obj)
except:
return False
return True
[stackoverflow: How to check if an object is pickleable]
(https://stackoverflow.com/questions/17872056/how-to-check-if-an-object-is-pickleable)
ではpickle.PicklingError
という例外をキャッチしているが,今回の例では他の例外 ('str' object is not callable
) も発生する.
基本ケース
class MyClass:
def __init__(self, val):
self.val = val
obj = MyClass(1)
print(is_picklable(obj)) # True
__getattr__
を追加で定義
class MyClassGA:
def __init__(self, val):
self.val = val
def __getattr__(self, attr):
return '{0} {1}'.format(attr, self.val)
obj = MyClassGA(1)
print(obj.foo) # foo1
print(is_picklable(obj)) # False
解決策1: __getattr__
内で特殊メソッドを除外する
class MyClassPickleble:
def __init__(self, val):
self.val = val
def __getattr__(self, attr):
if attr.startswith('__') and attr.endswith('__'):
raise AttributeError
return '{0} {1}'.format(attr, self.val)
obj = MyClassPickleble(1)
print(obj.foo) # foo 1
print(is_picklable(obj)) # True
解決策2: __getstate__
と__setstate__
を追加で定義
class MyClassPickleble2:
def __init__(self, val):
self.val = val
def __getattr__(self, attr):
return '{0} {1}'.format(attr, self.val)
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
return self.__dict__.update(d)
obj = MyClassPickleble2(1)
print(obj.foo) # foo
print(is_picklable(obj)) # True
[stackoverflow: Pickle of object with __getattr__ method in Python returns TypeError, object not callable
]
(https://stackoverflow.com/questions/50888391/pickle-of-object-with-getattr-method-in-python-returns-typeerror-object-no)
[銀月の符号: pickle できない属性をもつクラスへの対策例]
(https://fgshun.hatenablog.com/entry/20080812/1218559308)
[Python: Python deepcopy with custom __getattr__ and __setattr__]
(https://stackoverflow.com/questions/40583131/python-deepcopy-with-custom-getattr-and-setattr)
文字列による動的なメソッドの呼び出し
getattr(オブジェクト, 'メソッド名')(引数)
でOK.
adict = {'a': 1}
getattr(adict, 'get')('a', None)
[やつらかやつらだ: Pythonで動的にメソッドを呼ぶ]
(https://themorthem.hatenadiary.org/entry/20110209/1297264984)
クラス/インスタンスに後からメソッドを追加する
def fooFighters(self):
print "fooFighters"
A.fooFighters = fooFighters # クラス自体にfooFightersメソッドを追加
a.barFighters = types.MethodType(barFighters, a) # インスタンスだけにbarFightersメソッドを追加
a.barFighters = barFighters # これではselfが使えない (単なるプロパティとして追加されてしまう→メソッドだとは思われない)
[stackoverflow: Adding a Method to an Existing Object Instance]
(https://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance)
[Ian Lewis: Pythonでメソッドをクラスまたはインスタンスに動的に追加する]
(https://www.ianlewis.org/jp/python-add-class-method)
インスタンスへの追加にsetattr
を使う時
methods = [method0, method1]
for method in methods:
setattr(obj, method.__name__, types.MethodType(method, obj))
※これらの方法は特殊メソッドには適用不可
e.g. __add__
をあるobj
に追加すると,obj.__add__(item)
はできてもobj + item
は不可
上記はインスタンスにメソッドを追加するが,特殊メソッドを追加・上書きするにはクラスでの定義自体を変更する必要がある.
[stackoverflow: Setting special methods using setattr()]
(https://stackoverflow.com/questions/10484304/setting-special-methods-using-setattr)
[stackoverflow: Overriding special methods on an instance]
(https://stackoverflow.com/questions/10376604/overriding-special-methods-on-an-instance)
イテレータ
[Qiita@tchnkmr: [Python] イテレータを実装する]
(https://qiita.com/tchnkmr/items/e740173d7400f8672d75)
[くろのて: [Python] 部屋とYシャツとイテレータとジェネレータと私]
(https://note.crohaco.net/2016/python-iterator-generator-and-tshirt-me/)
[デジタリ: PythonのIteratorのちょっと詳しい説明]
(https://digitali.biz/python%E3%81%AEiterator%E3%81%AE%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E8%A9%B3%E3%81%97%E3%81%84%E8%AA%AC%E6%98%8E/)
list()
で__iter__
の全要素を取得可能
class MyClass:
def __init__(self): pass
def __iter__(self):
for i in range(3):
yield i
obj = MyClass()
for i in obj:
print(i)
# 0
# 1
# 2
obj = MyClass()
print(list(obj)) # [0, 1, 2]
__call__
経由で引数を渡しても期待通りの動作になる.
class MyClass:
def __init__(self, n=None):
self.n = n
def __iter__(self):
for i in range(self.n):
yield i
def __call__(self, n):
self.n = n
return self
obj = MyClass()
for i in obj(3):
print(i)
# 0
# 1
# 2
obj = MyClass()
print(list(obj(5))) # [0, 1, 2, 3, 4]
デコレータ
classmethod, staticmethod
DjangoBrothers: 【Python】インスタンスメソッド、staticmethod、classmethodの違いと使い方
property
Effective Pythonに
その他
getattr(obj, attr_name, default_value)
attributeが存在しないときにはデフォルト値を返す
import datetime as dt
a = dt.date(2000, 1, 1)
print(a.year)
print(getattr(a, 'years', 'not found'))
あるオブジェクトがiterableか確認
from collections.abc import Iterable # drop `.abc` with Python 2.7 or lower
isinstance(obj, Iterable)
[stackoverflow: In Python, how do I determine if an object is iterable?]
(https://stackoverflow.com/questions/1952464/in-python-how-do-i-determine-if-an-object-is-iterable)
時々紹介されているhasattr(obj, '__iter__')
は必ずしも正しくない.
イテレータは__iter__
ではなく__getitem__
でも動作するからである.
(但し,__getitem__
はint以外でも動作するため,__getitem__
があるからiterableだ,というのは誤り.)
[民主主義に乾杯: イテラブル, iterable ってなに?]
(https://python.ms/iterable/)