クラス (3)新スタイルクラス

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

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください