Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,27 @@ def test_array_assign(self):
m[:] = new_a
self.assertEqual(a, new_a)

def test_compare_equal(self):
# A memoryview is equal to itself: there is no need to compare
# individual values. This is not true for float values since they can
# be NaN, and NaN is not equal to itself.
for int_format in 'bBhHiIlLqQ':
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can "?" be tested? Can format starting with "@" be tested? Can the null format be tested?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to test these formats. array.array doesn't support "P" and "?" formats and it doesn't support "@" byte order. Do you have an idea how to test these cases?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memoryview.cast() supports them.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprisingly:

>>> memoryview(b'\0\1').cast('?') == memoryview(b'\0\2').cast('?')
False

even if

>>> list(memoryview(b'\0\1').cast('?')) == list(memoryview(b'\0\2').cast('?'))
True

But this may be platform depending, so I would not test values different than 0 and 1. Or 1 is also not safe?

It may be undefined behavior to interpret random values except 0 as void* (even if it works on x86). Maybe there is a way to create an array of pointers in ctypes? Or it is not worth to bother?

with self.subTest(format=int_format):
a = array.array(int_format, [1, 2, 3])
m = memoryview(a)
self.assertTrue(m == m)
Comment thread
vstinner marked this conversation as resolved.
Outdated

for float_format in 'fd':
with self.subTest(format=int_format):
a = array.array(float_format, [1.0, 2.0, float('nan')])
m = memoryview(a)
# nan is not equal to nan
self.assertFalse(m == m)
Comment thread
vstinner marked this conversation as resolved.
Outdated

a = array.array(float_format, [1.0, 2.0, 3.0])
m = memoryview(a)
self.assertTrue(m == m)


class BytesMemorySliceTest(unittest.TestCase,
BaseMemorySliceTests, BaseBytesMemoryTests):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Optimize :class:`memoryview` comparison: a :class:`memoryview` is equal to
itself, there is no need to compare values. Patch by Victor Stinner.
27 changes: 27 additions & 0 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3101,6 +3101,25 @@ cmp_rec(const char *p, const char *q,
return 1;
}

static int
is_float_format(const char *format)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this cover the complex types?

import numpy as np
a = np.array([1+2j, 3+4j, float('nan')], dtype=np.complex128)
mv = memoryview(a)
mv == mv # False

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This memory format is Zd. Oh, my change doesn't work for this memoryview. I should replace the blocklist with an allowlist. I'm not a memoryview/buffer expert. I didn't know that 3rd party projects can have their own format.

{
if (format == NULL) {
return 0;
}
if (strcmp("d", format) == 0) {
return 1;
}
if (strcmp("f", format) == 0) {
return 1;
}
if (strcmp("e", format) == 0) {
return 1;
}
return 0;
}


static PyObject *
memory_richcompare(PyObject *v, PyObject *w, int op)
{
Expand All @@ -3122,6 +3141,14 @@ memory_richcompare(PyObject *v, PyObject *w, int op)
}
vv = VIEW_ADDR(v);

// A memoryview is equal to itself: there is no need to compare individual
// values. This is not true for float values since they can be NaN, and NaN
// is not equal to itself.
if (v == w && !is_float_format(vv->format)) {
equal = 1;
goto result;
}

if (PyMemoryView_Check(w)) {
if (BASE_INACCESSIBLE(w)) {
equal = (v == w);
Expand Down
Loading