使用特殊设置功能创建自定义Counter对象

时间:2017-04-04 01:19:51

标签: python oop dictionary counter

enter image description here开始,@ AshwiniChaudhary给出了一个很好的答案来创建一个具有不同set()函数的新(X,Y) = (164,92)对象:

A = fft2(double(Im)/255.0);
>> Ashifted = fftshift(A);
>> Ashifted(97,158)
ans =   7.4484 - 10.4582i
>> Ashifted(93,165)
ans =  12.1928 -  4.9850i

为了允许用户定义的char / str附加到密钥,我尝试过:

Counter

但它正在返回一个空柜台:

from collections import Counter

class CustomCounter(Counter):
    def __setitem__(self, key, value):
        if len(key) > 1 and not key.endswith(u"\uE000"):
            key += u"\uE000"
        super(CustomCounter, self).__setitem__(key, value)

那是因为我错过了from collections import Counter, defaultdict class AppendedStrCounter(Counter): def __init__(self, str_to_append): self._appended_str = str_to_append super(AppendedStrCounter, self).__init__() def __setitem__(self, key, value): if len(key) > 1 and not key.endswith(self._appended_str): key += self._appended_str super(AppendedStrCounter, self).__setitem__(tuple(key), value) 中的iter:

>>> class AppendedStrCounter(Counter):
...     def __init__(self, str_to_append):
...         self._appended_str = str_to_append
...         super(AppendedStrCounter, self).__init__()
...     def __setitem__(self, key, value):
...         if len(key) > 1 and not key.endswith(self._appended_str):
...             key += self._appended_str
...         super(AppendedStrCounter, self).__setitem__(tuple(key), value)
... 
>>> AppendedStrCounter('foo bar bar blah'.split())
AppendedStrCounter()

[OUT]:

__init__()

但是from collections import Counter, defaultdict class AppendedStrCounter(Counter): def __init__(self, iter, str_to_append): self._appended_str = str_to_append super(AppendedStrCounter, self).__init__(iter) def __setitem__(self, key, value): if len(key) > 1 and not key.endswith(self._appended_str): key += self._appended_str super(AppendedStrCounter, self).__setitem__(tuple(key), value) 的值是错误的,它应该是2而不是1.

使用>>> AppendedStrCounter('foo bar bar blah'.split(), u'\ue000') AppendedStrCounter({('f', 'o', 'o', '\ue000'): 1, ('b', 'a', 'r', '\ue000'): 1, ('b', 'l', 'a', 'h', '\ue000'): 1}) 'bar'正确的方式初始化iter

1 个答案:

答案 0 :(得分:1)

正如指出的那样 Felix's commentcollections.Counter 没有记录 如何__init__方法添加键或设置值,只记录它。 由于它没有明确地设计用于子类化,最明智的做法是 not 子类化它。

collections.abc 模块的存在是为了提供易于子类化的Python内置类型的抽象类,包括dictMutableMapping,以ABC术语表示)。 所以,如果您只需要“Counter - 类” (而不是“Counter的子类,它将满足像isinstanceissubclass这样的内置函数,” 您可以创建自己的MutableMapping,其中包含Counter,然后是“中间人”初始值设定项以及Counter添加到典型dict的三种方法:

import collections
import collections.abc


def _identity(s):
    '''
    Default mutator function.
    '''
    return s


class CustomCounter(collections.abc.MutableMapping):
    '''
    Overrides the 5 methods of a MutableMapping:
    __getitem__, __setitem__, __delitem__, __iter__, __len__

    ...and the 3 non-Mapping methods of Counter:
    elements, most_common, subtract
    '''

    def __init__(self, values=None, *, mutator=_identity):
        self._mutator = mutator
        if values is None:
            self._counter = collections.Counter()
        else:
            values = (self._mutator(v) for v in values)
            self._counter = collections.Counter(values)
        return

    def __getitem__(self, item):
        return self._counter[self._mutator(item)]

    def __setitem__(self, item, value):
        self._counter[self._mutator(item)] = value
        return

    def __delitem__(self, item):
        del self._counter[self._mutator(item)]
        return

    def __iter__(self):
        return iter(self._counter)

    def __len__(self):
        return len(self._counter)

    def __repr__(self):
        return ''.join([
          self.__class__.__name__,
          '(',
          repr(dict(self._counter)),
          ')'
          ])

    def elements(self):
        return self._counter.elements()

    def most_common(self, n):
        return self._counter.most_common(n)

    def subtract(self, values):
        if isinstance(values, collections.abc.Mapping):
            values = {self._mutator(k): v for k, v in values.items()}
            return self._counter.subtract(values)
        else:
            values = (self._mutator(v) for v in values)
            return self._counter.subtract(values)


def main():
    def mutator(s):
        # Asterisks are easier to print than '\ue000'.
        return '*' + s + '*'

    words = 'the lazy fox jumps over the brown dog'.split()

    # Test None (allowed by collections.Counter).
    ctr_none = CustomCounter(None)
    assert 0 == len(ctr_none)

    # Test typical dict and collections.Counter methods.
    ctr = CustomCounter(words, mutator=mutator)
    print(ctr)
    assert 1 == ctr['dog']
    assert 2 == ctr['the']
    assert 7 == len(ctr)
    del(ctr['lazy'])
    assert 6 == len(ctr)
    ctr.subtract(['jumps', 'dog'])
    assert 0 == ctr['dog']
    assert 6 == len(ctr)
    ctr.subtract({'the': 5, 'bogus': 100})
    assert -3 == ctr['the']
    assert -100 == ctr['bogus']
    assert 7 == len(ctr)
    return


if "__main__" == __name__:
    main()

输出(换行,为便于阅读):

CustomCounter({
  '*brown*': 1,
  '*lazy*': 1,
  '*the*': 2,
  '*over*': 1,
  '*jumps*': 1,
  '*fox*': 1,
  '*dog*': 1
  })

我在初始化程序mutator中添加了一个仅限关键字的参数,用于存储将真实世界的whatevers转换为“突变”计数版本的函数。 请注意,这可能意味着CustomCounter不再存储“可哈希的对象”,而是“不能制作变异器barf的可哈希对象”。

此外,如果标准库的Counter获得了新方法,则必须更新CustomCounter以“覆盖”它们。 (你可以通过使用来解决这个问题 __getattr__ 将任何未知属性传递给self._counter,但参数中的任何键都将以原始的“未变异”形式传递给Counter

最后,正如我之前提到的,如果其他代码专门寻找一个代码,那么实际上不是 collections.Counter的子类。