__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 アトリビュートがないって怒られた。どうすりゃいいんだ。