旧スタイルのクラス定義というものを知っていますか
Python2 系では、class C:
と class C(object):
が異なる挙動をするということを知らずに、大ハマりしました。
参考:
- https://docs.python.jp/2.7/reference/datamodel.html#new-style-and-classic-classes
- https://stackoverflow.com/questions/4015417/python-class-inherits-object
旧スタイルのクラスの違い
class C:
や class C():
のように、object からの継承を省略すると、Python 2.1 以前と互換の旧スタイルのクラスとなります。
>>> class OldStyle:
... pass
...
>>> old = OldStyle()
>>> type(old)
<type 'instance'>
>>> dir(old)
['__doc__', '__module__']
>>> class NewStyle(object):
... pass
...
>>> new = NewStyle()
>>> type(new)
<class '__main__.NewStyle'>
>>> dir(new)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
旧形式は instance 型、新形式は、object から派生する class 階層下という違いなのですが、私がハマったのは、旧形式では、さりげなくデコレータが動かないこと。
@property の setter が動かない
class NewStyleClass(object):
def __init__(self):
self._v = 0
@property
def v(self):
print "get"
return self._v
@v.setter
def v(self, value):
print "set"
self._v = value
class OldStyleClass:
def __init__(self):
self._v = 0
@property
def v(self):
print "get"
return self._v
@v.setter
def v(self, value):
print "set"
self._v = value
>>> new = NewStyleClass()
>>> new.v
get
0
>>> new.v = 100
set
>>> new.v
get
100
>>> old = OldStyleClass()
>>> old.v
get
0
>>> old.v = 100 # ← setter が呼ばれていない
>>> old.v # ← 上書きされて getter が行方不明に
100
中途半端に getter が動いている分、たちが悪いです。。。
Python3 では新形式に統一
Python3 では、両方の書き方で新形式の振る舞いになりますので、安心です。
しかし、それ故に、Python3 向けのコードでは class C:
形式で書かれているため、望ましい書き方はそっちなのかと勘違いし、Python 2.7 向けのコードをリファクタリングして動かなくなるという悲劇が。
結論
Python2.7 では class を書くときには object を明示的に継承しましょう。