This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: new Boolean ABC in numbers module
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: josh.r, mark.dickinson, r.david.murray, rhettinger, serhiy.storchaka, smarie
Priority: normal Keywords:

Created on 2018-02-20 13:32 by smarie, last changed 2022-04-11 14:58 by admin.

Files
File name Uploaded Description Edit
proposal.py smarie, 2018-02-22 15:40 updated proposal after comments
Messages (13)
msg312416 - (view) Author: Sylvain Marie (smarie) * Date: 2018-02-20 13:32
This issue is created following the discussion [Python-ideas] Boolean ABC similar to what's provided in the 'numbers' module.

The following items are suggested:
 - adding a 'Boolean' ABC class to the 'numbers' module
 - register python 'bool' as a virtual subclass of both 'Boolean' and 'Integral'
 - register numpy bool ('np.bool_') as a virtual subclass of 'Boolean' only
 - rename 'Integral' 'Integer' and leave 'Integral' as an alias for backwards compatibility

Below is a proposal Boolean class:

---------------------

class Boolean(metaclass=ABCMeta):
    """
    An abstract base class for booleans.
    """
    __slots__ = ()

    @abstractmethod
    def __bool__(self):
        """Return a builtin bool instance. Called for bool(self)."""

    @abstractmethod
    def __and__(self, other):
        """self & other"""

    @abstractmethod
    def __rand__(self, other):
        """other & self"""

    @abstractmethod
    def __xor__(self, other):
        """self ^ other"""

    @abstractmethod
    def __rxor__(self, other):
        """other ^ self"""

    @abstractmethod
    def __or__(self, other):
        """self | other"""

    @abstractmethod
    def __ror__(self, other):
        """other | self"""

    @abstractmethod
    def __invert__(self):
        """~self"""


# register bool and numpy bool_ as virtual subclasses
# so that issubclass(bool, Boolean) = issubclass(np.bool_, Boolean) = True
Boolean.register(bool)

try:
    import numpy as np
    Boolean.register(np.bool_)
except ImportError:
    # silently escape
    pass

# bool is also a virtual subclass of Integral. np.bool_ is not.
Integral.register(bool)
msg312435 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2018-02-20 21:06
First off, link to discussion: https://groups.google.com/d/topic/python-ideas/-3QW3cxj3ko/discussion

1. bool is already a virtual subclass of Integral since it's an actual subclass of int (which is a virtual subclass of Integral); no need to explicitly register it

2. Don't try to register numpy's types for them; doing so would mean simply having numpy installed forces it to be imported if you import numbers, even if your script never uses numpy. Let numpy add registration for the type itself.

3. If Boolean is not a subclass of Integer/Integral, why is it in the numbers module at all? The discussion seemed to suggest putting it in numbers when the idea was that Boolean would subclass Integer/Integral; if it's not numeric at all, then the numbers module doesn't make sense.

4. Obviously, it's impossible to overload the not behavior (__bool__ is called directly to get a true bool, then the result is inverted, there is no special method for handling the not keyword), so it looks like the proposal is to make __invert__ part of the interface. Except bool itself doesn't behave in a way that would make __invert__ make sense as a drop in substitution for not; ~True produces -2, ~False produces -1, in neither case does it produce a bool result, and the result is always truthy. Changing this is impractical, since it would violate the int-like behavior of bool (which has been a historical guarantee).
msg312478 - (view) Author: Sylvain Marie (smarie) * Date: 2018-02-21 08:51
Thanks !

1. > ok
2. > ok
3. > That's simply 'the latest state' in the discussion. I guess that having Boolean in numbers is better than it being in its own module, since it is consistent with the other ABCs (and bool is a subclass of both Boolean and Integral-er). To quote Guido's answer in the discussion thread

GVR> A thought just occurred to me. Maybe we should just add a Boolean class to numbers?

(several positive answers)

SMA > I would rather suggest to keep that Boolean ABC class independent of Integral (see proposal in first post) to let it remain 'pure', i.e. represent logical booleans only. However nothing prevents us to register python bool as a virtual subclass of *both* Integral and Boolean - while np.bool would be registered as a virtual subclass of Boolean only. This would reflect quite well the reality - the fact that python bool is both a Boolean and an Integer, while numpy bool is only a Boolean.

GVR> OK, that could work. At this point I think you should just file an issue on bugs.python.org (but since Python 3.7 is in feature freeze, expect this to be put on the 3.8 track).

4. > Very good point. I do not know how do handle this at this point. As long as bool is a subclass of int (changing this is another discussion), the only way to go for now is probably to remove the 'invert' method from this ABC - if I am interpreting your answer correctly?


Finally let's note that the Integral > Integer renaming is now handled in a separate thread https://bugs.python.org/issue32891
msg312479 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-21 09:07
> - register numpy bool ('np.bool_') as a virtual subclass of 'Boolean' only

Be aware that np.bool_ behaves differently from Python's bool type in a number of ways. It may not make sense to have something that tries to abstract over both bool and np.bool_.

>>> import numpy as np
>>> np.bool_(True) + np.bool_(True)
True
>>> ~np.bool_(True)
False
>>> True + True
2
>>> ~True
-2
msg312493 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-02-21 12:44
If a Boolean ABC includes __and__, __or__ and __xor__ which implement conjunction, disjunction and exclusive disjunction, shouldn't it include __le__, __gt__, __ge__ and __lt__? They implement other logical operations: material implication, material nonimplication, converse implication and converse nonimplication.
msg312499 - (view) Author: Sylvain Marie (smarie) * Date: 2018-02-21 18:05
@Mark: you are right. That's why the proposed ABC does NOT inherit from Integral/Integer and focuses on boolean logic in its simplest form (not, and, or, xor). This one is common to both python bool and np.bool_.

@Serhiy: thanks for this interesting suggestion ! However having been in the software development world for 12 years, I never met these concepts in practice yet. I suspect that therefore I will not be the only one - I'm afraid that might make this ABC hard to understand. Besides, I am not sure that numpy bool and python bool implement this correctly:

>>> True > False
True
>>> np.bool_(True) > np.bool_(False)
True

This does not seem to comply with the description I found in https://en.wikipedia.org/wiki/Material_conditional

For these reasons I would suggest these operations to be part of a separate class.

-Sylvain
msg312501 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-02-21 18:35
Use <= for this operation.
msg312507 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-21 20:26
> This one is common to both python bool and np.bool_.

It's not, though. One of my examples was using `~` (`__invert__`) from the proposed ABC). And the behaviour of that function differs between NumPy and Python

I think one of the rules about this sort of abstraction should be that there should be multiple concrete examples available that you intend the abstraction to apply to, *before* you abstract. Otherwise there's a risk of making an abstraction that doesn't turn out to fit actual use-cases. Right now, we only have Python's bool as a concrete example; NumPy's bool_ is IMO not close enough in its behaviour. Are you aware of other bool-like types around that the proposed ABC would be helpful for?
msg312508 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-21 21:05
Adjusted title (since the Integer <-> Integral rename is the subject of a separate issue) and versions (since as a new feature, this can't go into Python 3.7, which is already in feature freeze).
msg312563 - (view) Author: Sylvain Marie (smarie) * Date: 2018-02-22 12:59
@Mark : the '__invert__' method is out of the game since Josh comment (and my reply https://bugs.python.org/issue32886#msg312478 )

So the remaining operations *are* an abstraction of both python bool and numpy bool_ behaviour (and a minimal representation of boolean logic operations that will seem natural to anyone, don't you think ?)
msg312627 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-23 10:59
> and a minimal representation of boolean logic operations that will seem natural to anyone, don't you think ?

I'm afraid I don't. :-(

Issue 1: this ABC doesn't seem a good match for Python bools. If I have a bool-like object in a piece of code, my main expectations of that object would be that (a) it can be interpreted in a boolean context (e.g., the condition of an if statement, a while statement, an if-else expression, a std. lib. call that expects a bool, etc.), and (b) it can be combined with other bool-likes via `and`, `or` and `not`. All I need for (a) and (b) to work is that `__bool__` is defined. The *bitwise* operations provided by `__and__`, `__or__` and friends are irrelevant for these purposes, and for Python's bool they exist mainly because bool is a subclass of int, and not to provide logical boolean operations. (NumPy's take on this is a bit different, because of the issues involved with interpreting an array of booleans in boolean context.)

Issue 2: an interface that includes `|`, `&` and `^` but not `~` seems incomplete. If I *am* aiming to do bitwise operations with an object (as opposed to logical operations) then I'd probably expect `~` to be present. (Admittedly, there are cases that support `|`, `^` and `&` but not `~`, like sets.)

Perhaps there's a place for an interface that declares that an object supports the *bitwise* operators: `|`, `&`, `^` and `~`. We *do* have more than one concrete class here that would implement such an interface: Python's integer types, NumPy's integer types, NumPy's `bool_` type, potentially other things that resemble fixed-length bit-strings (like IP addresses or collections of flags). But I wouldn't call such an interface "Boolean", and wouldn't expect Python's built-in bool type to be registered as supporting that interface.

So overall, call me -1 on this proposal.
msg315382 - (view) Author: Sylvain Marie (smarie) * Date: 2018-04-17 08:26
Mark
I get your point.

Mine is just to have a common abstraction between python's primitive bool and numpy bool (and possibly other future alternate booleans) for consistency with `numbers` and to be used in common type hints (PEP484). 

If I get you correctly, then the minimal `Boolean` ABC would only need to contain the `__bool__` method, without the bitwise operations. That's perfectly fine, as long as it correctly describes the minimal boolean logic (truth value for `if`/..., and `and`/`or`/`not`) and is compliant both with python and numpy bool.

A separate ABC for bitwise operations is then indeed a good idea to open in a separate thread. Finding a good name will be tough here though... `BitwiseOperable` ? `BitsContainer` ? ...  :)
Regards

-Sylvain
msg315536 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2018-04-20 20:29
> If I get you correctly, then the minimal `Boolean` ABC would only need
> to contain the `__bool__` method, without the bitwise operations

Once reduced to just having `__bool__`, I think there is no value added and would prefer to leave this out.  We really don't have to have an ABC for every single type in Python, something that will end-up just being clutter that we have to support in perpetuity.
History
Date User Action Args
2022-04-11 14:58:58adminsetgithub: 77067
2018-04-20 20:29:19rhettingersetnosy: + rhettinger
messages: + msg315536
2018-04-17 08:26:05smariesetmessages: + msg315382
2018-02-24 19:20:21r.david.murraysetnosy: + r.david.murray
2018-02-23 10:59:56mark.dickinsonsetmessages: + msg312627
2018-02-22 15:40:08smariesetfiles: + proposal.py
2018-02-22 12:59:47smariesetmessages: + msg312563
2018-02-21 21:05:45mark.dickinsonsettitle: new Boolean ABC in numbers module + Integral>Integer renaming -> new Boolean ABC in numbers module
messages: + msg312508
versions: - Python 3.7
2018-02-21 20:26:15mark.dickinsonsetmessages: + msg312507
2018-02-21 18:35:02serhiy.storchakasetmessages: + msg312501
2018-02-21 18:05:47smariesetmessages: + msg312499
2018-02-21 12:44:26serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg312493
2018-02-21 09:07:49mark.dickinsonsetnosy: + mark.dickinson
messages: + msg312479
2018-02-21 08:51:45smariesetmessages: + msg312478
2018-02-20 21:06:56josh.rsetnosy: + josh.r
messages: + msg312435
2018-02-20 13:32:01smariecreate