__setattr__
メタクラスでオーバーライドして、初期化中にすべての属性のデフォルトの型を確認するだけです。
>>> class Meta(type):
def __new__(meta, name, bases, dict):
def _check(self, attr, value):
if attr in self.defaults:
if not isinstance(value, self.defaults[attr]):
raise TypeError('%s cannot be %s' % (attr, type(value)))
else:
self.defaults[attr] = type(value)
def _setattr(self, attr, value):
_check(self, attr, value)
object.__setattr__(self, attr, value)
cls = type.__new__(meta, name, bases, dict)
# Set up default type for every attribute
cls.defaults = {name: type(value) for name, value in dict.items()}
cls.__setattr__ = _setattr
return cls
>>> class Image(object):
__metaclass__ = Meta
height = 0
width = 0
path = '/tmp'
size = 0
>>> i = Image()
>>> i.height = 240
>>> i.height
240
>>> i.size
0
>>> i.size = 7
>>> i.size
7
>>> i.path = '/tmp/subdir'
>>> i.path
'/tmp/subdir'
>>> i.path = 23
TypeError: path cannot be <type 'int'>
代替の(そしておそらくよりエレガントな)方法:
class MetaBase(object):
def _check(self, attr, value):
if attr in self.defaults:
if not isinstance(value, self.defaults[attr]):
raise TypeError('%s cannot be %s' % (attr, type(value)))
else:
self.defaults[attr] = type(value)
def __setattr__(self, attr, value):
self._check(attr, value)
super(MetaBase, self).__setattr__(attr, value)
class Meta(type):
def __new__(meta, name, bases, dict):
cls = type.__new__(meta, name, (MetaBase,) + bases, dict)
cls.defaults = {name: type(value) for name, value in dict.items()}
return cls
class Image(object):
__metaclass__ = Meta
height = 0
width = 0
path = '/tmp'
size = 0
挙動は以前と同じ