pythonxorbitarray

Strange behavior with lists of Python bitarrays and the in-place XOR operation ^=


I've managed to isolate (what I perceive to be) a strange relationship between lists of bitarrays and the XOR operation in Python.

Here is my minimal example:

ls = [bitarray('0'), bitarray('0')]
ls[0] ^= bitarray('1')
print(ls)
> [bitarray('1'), bitarray('0')]

ls = [bitarray('0')]*2
ls[0] ^= bitarray('1')
print(ls)
> [bitarray('1'), bitarray('1')]

ls = [bitarray('0'), bitarray('0')]
ls[0] = ls[0] ^ bitarray('1')
print(ls)
> [bitarray('1'), bitarray('0')]

I have two natural questions:

  1. Why does the second code block produce a different result than the first? I thought perhaps the * operation simply duplicates the reference (which would explain why both list elements were changed), but I can't replicate the behavior using just (integer) 1s and 0s.
  2. Why does expanding ls[0] ^= bitarray('1') to ls[0] = ls[0] ^ bitarray('1') revert the output to the same as the first code block? I understood ^= to be simply shorthand for the latter expression.

Solution

  • This has nothing to do with ^=, but with how you created the lists.

    The first example is a list containing references to two separate objects:

    >>> ls = [object(), object()]
    >>> ls[0] is ls[1]
    False
    

    The second example is a list containing two references to the same object:

    >>> ls = [object()] * 2
    >>> ls[0] is ls[1]
    True
    

    ls[0] = ls[0] ^ y makes ls[0] refer to a new object, regardless of what ls[1] refers to. ls[0] ^= y modifies a mutable object ls[0], so changes to it are visible via any reference to the value (including ls[1] when applicable).