Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

c-types Structure and equality with bytes/bytearray

119 views
Skip to first unread message

Michael Hull

unread,
Apr 26, 2021, 1:32:47 PM4/26/21
to
Hello everyone,

I've been working with ctypes recently, and I've come across what seems to
be some slightly confusing behaviour, when comparing ctype's Structure
against `bytes` and `bytearray` objects


import ctypes



class Int(ctypes.Structure):

_fields_ = [("first_16", ctypes.c_int8),

("second_16", ctypes.c_int8)]



def serialise(self):

return ctypes.string_at(ctypes.addressof(self),

ctypes.sizeof(self))



i_obj= Int(first_16= 65, second_16=66)

print(i_obj.first_16, i_obj.second_16)



i_bytes = i_obj.serialise()

print(list(i_bytes))

assert type(i_bytes) == bytes



i_bytearray = bytearray(i_bytes)





print("obj == bytes =>", i_obj==i_bytes)

print("bytes == obj =>", i_bytes==i_obj)

print("obj == bytearray =>", i_obj==i_bytearray)

print("bytearray == obj =>", i_bytearray==i_obj)




When I run this (Python 3.6, and Python2.7) , the final 4 print statements
give the following:

"""

obj == bytes => False

bytes == obj => False

obj == bytearray => True

bytearray == obj => True

"""

Is this a bit strange -- my understanding was that `bytes` and `bytearray`
would normally be expected to work quite interchangeably with each other?


It also means that:

bytearray == obj => True

bytes == bytearray => True

bytes == obj => False


If anyone has any experience in this area, and can let me know if I'm
overlooking something silly, and guidance would be gratefully appreciated.
Alternatively, I am happy to open up a bug-report if this looks like
unexpected behaviour?


Best wishes,



Mike

Eryk Sun

unread,
Apr 29, 2021, 5:56:36 PM4/29/21
to
On 4/26/21, Michael Hull <mikeh...@gmail.com> wrote:
>
> my understanding was that `bytes` and `bytearray` would normally
> be expected to work quite interchangeably with each other?

bytearray.__eq__() is more flexible:

>>> i = Int16(first=65, second=66)
>>> bytearray(i).__eq__(i)
True

>>> i.__eq__(bytearray(i))
NotImplemented
>>> i.__eq__(bytes(i))
NotImplemented
>>> bytes(i).__eq__(i)
NotImplemented

When in doubt, read the source code. In bytearray_richcompare() in
Objects/bytearrayobject.c, the comparison begins by using the buffer
protocol to get comparable byte strings. ctypes data types support the
buffer protocol, so this works well. On the other hand,
bytes_richcompare() in Objects/bytesobject.c does not use the buffer
protocol but instead requires the other object to be a bytes object.

In Python 3.3+, you can force the comparison to use the buffer
protocol via memoryview(). For example:

>>> memoryview(i).cast('B') == bytes(i)
True
>>> memoryview(i).cast('B') == bytearray(i)
True

The cast() to "B" (unsigned char) is required because views of ctypes
data objects include their format:

>>> memoryview(i).format
'T{<b:first:<b:second:}'
>>> memoryview(i).cast('B').format
'B'
0 new messages