Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return NotImplemented for SortedSet.__and__ #219

Open
SimpleArt opened this issue Mar 11, 2023 · 3 comments
Open

Return NotImplemented for SortedSet.__and__ #219

SimpleArt opened this issue Mar 11, 2023 · 3 comments

Comments

@SimpleArt
Copy link

Binary operators such as __eq__, __and__, etc. should return NotImplemented in the event that the given argument is an unsupported type rather than raising an error. This allows the other object to patch in its own implementation.

Roughly speaking, it means code should be written like this:

from typing import Iterable


class SortedSet:

    def __and__(self, other):
        if isinstance(other, Iterable):  # Or `hasattr(type(other), "__iter__")`.
            return self.intersection(other)
        else:
            return NotImplemented  # `self & other` then checks `other.__and__(self)`.

This allows cases such as this:

class Interval:  # Not a set e.g. does not contain `__iter__`.

    def __and__(self, other):
        if isinstance(other, Interval):
            ...
        elif isinstance(other, Iterable):
            return {x for x in other if x in self}
        else:
            return NotImplemented

    def __contains__(self, other):
        ...


sorted_set & interval  # Expected set, got TypeError.
@grantjenks
Copy link
Owner

I'm familiar with return NotImplemented and agree it would be helpful. Here's the code:

    def intersection(self, *iterables):
        """Return the intersection of two or more sets as a new sorted set.

        The `intersection` method also corresponds to operator ``&``.

        ``ss.__and__(iterable)`` <==> ``ss & iterable``

        The intersection is all values that are in this sorted set and each of
        the other `iterables`.

        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.intersection([4, 5, 6, 7])
        SortedSet([4, 5])

        :param iterables: iterable arguments
        :return: new sorted set

        """
        intersect = self._set.intersection(*iterables)
        return self._fromset(intersect, key=self._key)

    __and__ = intersection
    __rand__ = __and__

SortedSet.__and__ calls self._set.intersection(*iterables) and that's what raises the TypeError.

It may be easy enough to fix with:

    def __and__(self, other):
        """Return the intersection of two sets as a new sorted set.

        ``ss.__and__(iterable)`` <==> ``ss & iterable``
        """
        intersect = self._set & other
        return self._fromset(intersect, key=self._key)

But it's disappointing the __and__ = intersection trick doesn't work.

@grantjenks grantjenks changed the title Fully support binary operators Return NotImplemented for SortedSet.__and__ Mar 13, 2023
@SimpleArt
Copy link
Author

I think maybe it ought to be:

    def __and__(self, other):
        """Return the intersection of two sets as a new sorted set.

        ``ss.__and__(iterable)`` <==> ``ss & iterable``
        """
        intersect = self._set.__and__(other)
        if intersect is NotImplemented:
            return NotImplemented
        return self._fromset(intersect, key=self._key)

@SimpleArt
Copy link
Author

Actually I don't believe set & iterable works, only set & set works, so it'd have to be either set.intersection(iterable) with a try block or an if check beforehand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants