装饰者动态超类

时间:2012-09-05 09:27:36

标签: python decorator

我有简单的装饰器,用于在尝试检索dict值时替换无效字符。

import types


class ContentInterface(dict):
    def __getitem__(self, item):
        raise NotImplementedError


class Content(ContentInterface):
    def __getitem__(self, item):
        return dict.__getitem__(self, item)


class DictDecorator(ContentInterface):
    def __init__(self, interfaceContent, **config):
        super(DictDecorator, self).__init__()
        self._component = interfaceContent
        self._config = config

    def _replace(self, text):
        return text

    def _check(self, invalidCharacterSet, itemPath):
        pass

    def __getitem__(self, name):
        item = self._component[name]
        if isinstance(item, types.StringTypes):
            newText = self._replace(item)
            invalidCharacterSet = set([char for char in item if char not in newText])
            self._check(invalidCharacterSet, name)
            return newText
        else:
            return self.__class__(item, **self._config)


class ReplaceCommaDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(",", ' ')


class ReplaceDotDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace('.', ' ')


class ReplaceColonDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(":", ' ')


class ReplaceSemicolonDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(";", ' ')

我想以下列方式使用它:

dictWithReplacedCharacters =\
ReplaceCommaDecorator(                       # Empty
    ReplaceDotDecorator(                     # Empty
        ReplaceColonDecorator(               # Empty
            ReplaceSemicolonDecorator(       # Empty
                Content({                    # Data
                    '1':u'1A:B;C,D.E',
                    '2':{
                        '21':u'21A:B;C,D.E',
                        '22':u'22A:B;C,D.E',
                    }
                }),
            ),
        ),
    ),
)

print dictWithReplacedCharacters['2']['21']

我有4个冗余的dict对象代表一个数据字典的装饰器。

我想强制上面的嵌套语句返回从继承自包含数据的Content的ReplaceSemicolonDecorator继承的ReplaceColonDecorator继承的ReplaceDotDecorator继承的ReplaceCommaDecorator对象。我想这可以在DictDecorator的__new__方法中解决。

1 个答案:

答案 0 :(得分:0)

我不认为你所做的是正确的方法。我只想编写一个接受一组通用字符的类并正确地进行检查/替换,所以如果你想要转义更多的字符,你必须简单地传递一个包含所有字符的集合(加上可能有关如何替换的一些元信息)等)。

在您的示例中,代码不起作用,因为str.replace需要两个参数,而您只传递一个。也许你想删除这个角色?在这种情况下,您应该replace(character, "")。无论如何,如果您打算只用单个字符替换单个字符,那么您可以查看str.translate模块中的maketrans方法和string函数。

否则可能是正则表达式(使用re.sub)。

无论如何,你要求的方法就是:

class ContentInterface(dict):
    pass

class Content(ContentInterface):
    pass

class DictDecorator(ContentInterface):

    def __new__(cls, other):
        if isinstance(other, DictDecorator):
            if not issubclass(cls, other.__class__) and not issubclass(other.__class__, cls):
                new_cls = type('NewType', (cls, other.__class__), {})
                return new_cls(other)

        ob = dict.__new__(cls)
        return ob

    def __init__(self, interface, **config):
        super(DictDecorator, self).__init__()
        self._component = interface
        self._config = config

        # just to make the result appear with the content.
        # You may not need this.
        self.update(self._component)

    def _replace(self, text):
        return text

    def _check(self, invalid_char_set, item_path):
        pass

    def __getitem__(self, name):
        item = self._component[name]
        if isinstance(item, basestring):
            new_text = self._replace(item)
            invalid_char_set = set(char for char in item if char not in new_text)
            self._check(invalid_char_set, name)
            return new_text
        else:
            # What you wrote here does not make sense...
            # maybe you wanted: return self._config[name]?
            return super(DictDecorator, self).__getitem__(name)


class ReplaceCommaDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(",", ' ')


class ReplaceDotDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace('.', ' ')


class ReplaceColonDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(":", ' ')


class ReplaceSemicolonDecorator(DictDecorator):
    def _replace(self, text):
        return text.replace(";", ' ')

似乎有效:

>>> a = Content({'1': u'1A:B;C,D.E', '2': {'21': u'21A:B;C,D.E', '22': u'22A:B;C,D.E'}})
>>> r = ReplaceDotDecorator(ReplaceCommaDecorator(a))
>>> r['1']
u'1A:B;C D E'
>>> r = ReplaceColonDecorator(ReplaceDotDecorator(ReplaceCommaDecorator(a)))
>>> r['1']
u'1A B;C D E'

可能有更好的方法可以做到这一点,也许使用元类,但这是我能想到的唯一方法。

编辑:

顺便说一下,如果您不确切知道自己在做什么,我认为继承内置类型并不是一个好主意:

>>> class MyDict(dict): pass
... 
>>> a = MyDict(a=1, b=2)
>>> a
{'a': 1, 'b': 2}
>>> a.__class__
<class '__main__.MyDict'>
>>> copy = a.copy()
>>> copy.__class__     # Wrong class.
<type 'dict'>

或另一个例子:

>>> class MyDict(dict):
...     def __getitem__(self, key):
...             if key.startswith('a'):
...                     return 'BAD!'
...             return super(MyDict, self).__getitem__(key)
... 
>>> my = MyDict(a=1, b=2)
>>> my['a']
'BAD!'
>>> my['b']
2
>>> my.keys()
['a', 'b']
>>> my.values()   #you'd expect ['BAD!', 2]
[1, 2]

因此,请务必重新实现您将要使用的每种方法,这可能会导致类似的事情。