Home > Mobile >  What exactly is the bitwise AND doing with collections.Counter?
What exactly is the bitwise AND doing with collections.Counter?

Time:05-19

There was a recent question where the, correct, answer really surprised me by using & on two Counters and then "getting it right".

From the docs:

Counters support rich comparison operators for equality, subset, and superset relationships: ==, !=, <, <=, >, >=. All of those tests treat missing elements as having zero counts so that Counter(a=1) == Counter(a=1, b=0) returns true.

But that doesn't go into the specifics of &. I wrote a small test script:

from collections import Counter
from pprint import pp

cls = Counter  # `dict` fails: TypeError: unsupported operand type

o1 = cls(a=1,b=2,c=3,de=3,f=3,i1=9)
o2 = cls(a=1,b=2,c=3,de=5,f=6,i2=9)
res = o1 & o2

pp(dict(o1=o1,o2=o2,res=res))

the output is:

{'o1': Counter({'i1': 9, 'c': 3, 'de': 3, 'f': 3, 'b': 2, 'a': 1}),
 'o2': Counter({'i2': 9, 'f': 6, 'de': 5, 'c': 3, 'b': 2, 'a': 1}),
 'res': Counter({'c': 3, 'de': 3, 'f': 3, 'b': 2, 'a': 1})}

It seems to me that counter1 & counter2 means:

  • calculate the intersection of the keys of both.
  • for the values on common keys, compute the min

Am I correct? Asides from Counter, and set, do any other standard library data structures also define __and__ (the backing dunder for &, IIRC)?

CodePudding user response:

Your understanding is pretty much correct. If you look a couple lines down from where you've quoted, you'll see an example use of & on Counter objects -- you don't need to dive into the source code to find it:

Intersection and union return the minimum and maximum of corresponding counts. ...

>>> c & d                       # intersection:  min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

CodePudding user response:

Container docs state:

Several mathematical operations are provided for combining Counter objects to produce multisets (counters that have counts greater than zero). Addition and subtraction combine counters by adding or subtracting the counts of corresponding elements. Intersection and union return the minimum and maximum of corresponding counts. Equality and inclusion compare corresponding counts. Each operation can accept inputs with signed counts, but the output will exclude results with counts of zero or less.

To address your question about other dict derivatives overriding &, I don't believe so. However, set (docs here) uses &, |, - and ^ to implement intersection, union, difference and symmetric difference.

CodePudding user response:

Per official docs:

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c   d                       # add two counters together:  c[x]   d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})
>>> c == d                      # equality:  c[x] == d[x]
False
>>> c <= d                      # inclusion:  c[x] <= d[x]
False

Unary operations are also defined:

>>> c = Counter(a=2, b=-4)
>>>  c
Counter({'a': 2})
>>> -c
Counter({'b': 4})
  • Related