2012-04-30 11 views
4

私はPythonでメタクラスプログラミングを始めようとしています。メタクラスで属性の種類を制限する方法を知りたいと思います。 ディスクリプタはかなり簡単ですが、メタクラスはどうですか?ここでメタクラスで属性の種類を制限する

は短い例です:

>>> class Image(Object): 
    ...  height = 0 
    ...  width = 0 
    ...  path = '/tmp' 
    ...  size = 0 

    >>> img = Image() 
    >>> img.height = 340 
    >>> img.height 
    340 
    >>> img.path = '/tmp/x00.jpeg' 
    >>> img.path 
    '/tmp/x00.jpeg' 
    >>> img.path = 320 
    Traceback (most recent call last): 
     ... 
    TypeError 

Pythonのバージョンは、メタクラスで2.7

+0

私はこれが教育のためのものだと仮定しています。実際にPythonに静的型検査を行うつもりはありません。 – delnan

+0

@delnan:はい、そうです。 – cval

+1

メタクラスを使用して記述子を設定することを意味しますか? –

答えて

8

ただ、メタクラスで__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 

動作が

前と同じです
3

上書き__setattr__です。別にあなたが(あなたの例ではheight=0, path = '/tmp')の初期値を確認する必要がありますことに注意してください:

class RestrictAttrs(type): 
    def __new__(mcs, name, bases, dct): 
     def _checkattr(k, v): 
      if k == 'path': 
       if not isinstance(v, str): 
        raise TypeError('path must be a str!') 

     def _setattr(self, k, v): 
      _checkattr(k, v) 
      self.__dict__[k] = v 

     # Check of initial values (optional) 
     for k,v in dct.items(): 
      _checkattr(k, v) 

     res = type.__new__(mcs, name, bases, dct) 
     res.__setattr__ = _setattr 
     return res 

class Image(object): 
    __metaclass__ = RestrictAttrs 
    path = '/tmp' 

i = Image() 
i.path = 32 
関連する問題