__slots__
Python のクラスには旧スタイルクラスと新スタイルクラスがあるらしい。ややこしいな。
新スタイルクラスを作るには、ビルトイン型を継承するか、もしくは最上位のクラスであるobjectを継承する。
class Person(object): __slots__ = ["name", "age"] def __init__(self, name, age): self.name = name self.age = age def greeting(self): print "Hello, I'm " + self.name + "!"
新スタイルクラスの機能の一つとして、__slots__によってアクセスできるアトリビュートを制限できるというのがある。上の例では、アクセス可能なアトリビュートを name と age に制限している。これで勝手なアトリビュートを追加したりはできなくなったはずだ。やってみよう。
>>> from person2 import Person >>> andy = Person("Andy", 32) >>> andy.name 'Andy' >>> andy.age 32 >>> andy.job = "programmer" Traceback (most recent call last): File "", line 1, in AttributeError: 'Person' object has no attribute 'job'
__slots__ で指定している name と age にはアクセスできるけど、job に代入しようとしたら ‘job’なんてアトリビュート無いよ、と怒られた。
プロパティ
ただし、まだ name と age には値を代入できてしまう。
>>> andy.name = "Bill" >>> andy.name 'Bill'
名前は変わらないのだから、参照はできても代入はできてほしくない。こういうときはプロパティにすればいいらしい。
class Person(object): __slots__ = ["_name", "age"] def __init__(self, name, age): self._name = name self.age = age def greeting(self): print "Hello, I'm " + self._name + "!" def get_name(self): return self._name name = property(get_name, None)
名前用のアトリビュートを _name に変更して、参照用の get_name メソッドを作った。そしてproperty関数を使ってnameというアクセサを作っている。property関数の引数にはゲッター、セッターのメソッド指定する。セッターとしては None を指定しているから、これで name は参照だけできるようになったはずだ。
>>> from person2a import Person >>> andy = Person("Andy", 32) >>> andy.name 'Andy' >>> andy.name = "Bill" Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute
name に代入しようとしたら、アトリビュートをセットできない、といわれた。うまくいってるようだ。
でもまてよ。_name アトリビュートにはアクセスできるはずだから、代入もできてしまうんじゃないか?
>>> andy._name = "Bill" >>> andy.name 'Bill'
できちゃうじゃん。だめじゃん。
じゃあ、__slots__ から _name を取り去ったらどうだ。
class Person(object): __slots__ = ["age"] def __init__(self, name, age): self._name = name self.age = age def greeting(self): print "Hello, I'm " + self._name + "!" def get_name(self): return self._name name = property(get_name, None)
>>> from person2b import Person >>> andy = Person("Andy", 32) Traceback (most recent call last): File "", line 1, in File "person2b.py", line 6, in __init__ self._name = name AttributeError: 'Person' object has no attribute '_name'
だめだ。今度は _name アトリビュートがないって怒られた。どうすりゃいいんだ。