Añadir contadores borra teclas

Vea a continuación, ¿por qué la implementación de += sopla una tecla en mi contador original?

 >>> c = Counter({'a': 0, 'b': 0, 'c': 0}) >>> c.items() [('a', 0), ('c', 0), ('b', 0)] >>> c += Counter('abba') >>> c.items() [('a', 2), ('b', 2)] 

Creo que eso es descortés por decir lo menos, hay una gran diferencia entre “X se contó 0 veces” y “ni siquiera estamos contando Xs”. Parece que las collections.Counter Contador no es un contador en absoluto, es más como un multiset.

Pero los contadores son una subclase de dict y se nos permite construirlos con valores cero o negativos: Counter(a=0, b=-1) . Si en realidad es una “bolsa de cosas”, ¿esto no estaría prohibido, restringiendo el inicio para aceptar un iterable de elementos hashable?

Para confundir aún más las cosas, el contador implementa métodos de update y subtract que tienen un comportamiento diferente a los operadores + y - . ¡Parece que esta clase está teniendo una crisis de identidad!

¿Es un contador un dict o una bolsa?

    De la fuente ;

     def __add__(self, other): '''Add counts from two counters. >>> Counter('abbb') + Counter('bcc') Counter({'b': 4, 'c': 2, 'a': 1}) ''' if not isinstance(other, Counter): return NotImplemented result = Counter() for elem, count in self.items(): newcount = count + other[elem] if newcount > 0: result[elem] = newcount for elem, count in other.items(): if elem not in self and count > 0: result[elem] = count return result 

    Parece que el contador se implementó como eliminación de claves que sum cero claves no positivas. Como el valor predeterminado es cero y la fuente también tiene cero, el dict resultante no contiene esa clave.

    Quizás puedas obtener el mismo comportamiento con la actualización:

     a.update(b) 

    Parece hacer lo que quieras. Probablemente más lento aunque, una implementación hecha a mano del método __add__ sería mucho más rápida.

    Counter s son una especie de multiset. De la documentación del Counter() :

    Se proporcionan varias operaciones matemáticas para combinar objetos Counter para producir multisets (contadores que tienen conteos mayores que cero). La sum y la resta combinan contadores sumndo o restando los conteos de los elementos correspondientes. Intersección y sindicato devuelven el mínimo y máximo de cuentas correspondientes. Cada operación puede aceptar entradas con conteos firmados, pero la salida excluirá los resultados con conteos de cero o menos .

    Énfasis mío.

    Más adelante, le indica que le brinda más detalles sobre la naturaleza de múltiples conjuntos de Counter s:

    Nota : los contadores se diseñaron principalmente para trabajar con enteros positivos para representar los conteos en ejecución; sin embargo, se tuvo cuidado de no excluir innecesariamente los casos de uso que necesiten otros tipos o valores negativos. Para ayudar con esos casos de uso, esta sección documenta el rango mínimo y las restricciones de tipo.

    […]

    • Los métodos multiset están diseñados solo para casos de uso con valores positivos. Las entradas pueden ser negativas o cero, pero solo se crean salidas con valores positivos. No hay restricciones de tipo, pero el tipo de valor debe admitir la sum, la resta y la comparación.

    Así que los objetos de Counter son ambos ; Diccionarios y bolsos. Los diccionarios estándar, sin embargo, no admiten la adición, pero sí los de Counter , así que no es como si los Counter s estuvieran rompiendo una prioridad establecida por los diccionarios aquí.

    Si desea conservar los ceros, use Counter.update() y pase el resultado de Counter.elements() del otro objeto:

     c.update(Counter('abba').elements()) 

    Manifestación:

     >>> c = Counter({'a': 0, 'b': 0, 'c': 0}) >>> c.update(Counter('abba').elements()) >>> c Counter({'a': 2, 'b': 2, 'c': 0})