On 08.05.20 19:01, Steven D'Aprano wrote:

All this proposal adds is *duck-typing* to the comparison, for when
it doesn't matter what the container type is, you care only about the
values in the container. Why be forced to do a possibly expensive (and
maybe very expensive!) manual coercion to a common type just to check
the values for equality element by element, and then throw away the
coerced object?

If you have ever written `a == list(b)` or similar, then You Already
Needed It :-)

Initially I assumed that the reason for this new functionality was
concerned with cases where the types of two objects are not precisely
known and hence instead of converting them to a common type such as
list, a direct elementwise comparison is preferable (that's probably
uncommon though). Instead in the case where two objects are known to
have different types but nevertheless need to be compared
element-by-element, the performance argument makes sense of course.

So as a practical step forward, what about providing a wrapper type
which performs all operations elementwise on the operands. So for example:

    if all(elementwise(chars) == string):
        ...

Here the `elementwise(chars) == string` part returns a generator which
performs the `==` comparison element-by-element.

This doesn't perform any length checks yet, so as a bonus one could add
an `all` property:

    if elementwise(chars).all == string:
        ...

This first checks the lengths of the operands and only then compares for
equality. This wrapper type has the advantage that it can also be used
with any other operator, not just equality.

Here's a rough implementation of such a type:

    import functools
    import itertools
    import operator


    class elementwise:
        def __init__(self, obj, *, zip_func=zip):
            self.lhs = obj
            self.zip_func = zip_func

        def __eq__(self, other): return self.apply_op(other,
op=operator.eq)
        def __lt__(self, other): return self.apply_op(other,
op=operator.lt)
        ...  # define other operators here

        def apply_op(self, other, *, op):
            return self.make_generator(other, op=op)

        def make_generator(self, other, *, op):
            return itertools.starmap(op, self.zip_func(self.lhs, other))

        @property
        def all(self):
            zip_func = functools.partial(itertools.zip_longest,
fillvalue=object())
            return elementwise_all(self.lhs, zip_func=zip_func)


    class elementwise_all(elementwise):
        def apply_op(self, other, *, op):
            try:
                length_check = len(self.lhs) == len(other)
            except TypeError:
                length_check = True
            return length_check and all(self.make_generator(other, op=op))
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/CCPJWQ5TYCJHEUVZD554EEBYUIPIJIKP/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to