classification
Title: Truth value of sets not properly documented
Type: enhancement Stage:
Components: Documentation Versions: Python 3.7, Python 3.6, Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: terry.reedy Nosy List: docs@python, rhettinger, terry.reedy, thomassen
Priority: low Keywords:

Created on 2017-06-29 13:36 by thomassen, last changed 2017-07-21 15:24 by rhettinger.

Pull Requests
URL Status Linked Edit
PR 2508 open python-dev, 2017-06-30 11:02
Messages (7)
msg297268 - (view) Author: Peter Thomassen (thomassen) * Date: 2017-06-29 13:36
The truth value of sets is not properly documented, in particular regarding whether an empty set is considered false or not.

Ignoring primitive (such as numerals) as well as user-defined types, https://docs.python.org/3/library/stdtypes.html#truth says:

> The following values are considered false:
> 
> - [...]
> - any empty sequence, for example, '', (), [].
> - any empty mapping, for example, {}.
> - [...]
> 
> All other values are considered true

According to https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range, a set is not a sequence (it is unordered, its elements do not have indices, etc.):

> There are three basic sequence types: lists, tuples, and range objects.

And, according to https://docs.python.org/3/library/stdtypes.html#mapping-types-dict,

> There is currently only one standard mapping type, the dictionary.

So, as per the documentation, the set type is not a type that can ever be False. However, when I try, bool(set()) evaluates to False.

When I asked this on Stack Overflow, someone checked in the CPython code and judged that this is most likely a mere documentation issue: https://stackoverflow.com/a/44813565/6867099
msg297312 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2017-06-29 20:19
This case was supposed to be covered by the last bullet point, "instances of user-defined classes, if the class defines a __bool__() or __len__() method, when that method returns the integer zero or bool value False.".  The word "user-defined" should be dropped.

Also, the whole section can be simplified to something like:

"""
By default, objects are considered true unless they define either a __bool__ method that returns False or __len__ method that returns zero.  

Practically, this means that empty containers are false (such as [], (), {}, '', etc) and that numbers equal to zero are false (such as 0, 0.0, 0.0j, False, Decimal(0), Fractions(0, 1), etc).  Also, *None* is a false value.


"""
msg297391 - (view) Author: Peter Thomassen (thomassen) * Date: 2017-06-30 11:19
I submitted a PR on github, and signed the CLA before doing so. (I double-checked my bpo username in the CLA, and my github username in the bpo profile.) Still, the bot says I need to sign the CLA. I'm not sure what to do?
msg297448 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-06-30 22:14
1. You have to go to your profile page
https://bugs.python.org/user26480
and add your GitHub name in the GitHub Name box.
2. A committer has to change the labels to trigger the robot to recheck.  I did that but it did not work because of 1.

As I said in my review, I strongly prefer leaving the bulleted list and making a minimal addition of 'set or' and 'set(), '.  I would not merge the current patch.
msg298099 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-07-11 02:03
Responding here to Peter's PR comment.  Peter opened this issue with the claim that the doc failed a specific test case-- document the truth value of set().  Since mappings are (or can be viewed as) a specialized type of set, I always considered that the empty mapping line implicitly covered sets.  But I acknowledge that this is not clear for everyone.  The simplest fix to make the test pass would be: "any unordered collection, for example, set(), {}".  This should also cover frozenset and a possible frozendict.

Raymond noted that 'user-defined' in the last bullet point is wrong (it implies that built-in functions are different) and should be deleted.  He then combined the corrected rule for false with the default rule in the next sentence to produce a succinct statement of the actual rule.  (In CPython, 'default' is literally true. Class 'object' has neither __bool__ nor __len__; ditto for all subclasses that do not add one.)

With a minor change, I like this statement and agree that it should be moved above the examples.  But I think the bullet points should be reduced to just 3, rewritten, and single spaced, but not smashed into running text.  I suggest replacing everything between the first sentence (ending with 'below.') and the last two (beginning with 'Operations') with:

"By default, an object is considered true unless its class defines either a __bool__ method that returns False or __len__ method that returns zero, when called with the object.  Here are most of the built-in objects considered false.

* constants defined to be false: None and False.
* numeric 0 of any type: 0, 0.0, Decimal(0), Fractions(0, 1)
* empty sequences and collections: '', (), [], {}, set(), range(0)
"

Before writing the above, I checked that an instance attribute __bool__ = lambda: False is not consulted by bool().
msg298327 - (view) Author: Peter Thomassen (thomassen) * Date: 2017-07-14 01:13
I like your most recent suggestion, and updated the PR after fixing a typo ('Fractions') and making it more complete (complex numbers).

Let me know if anything else is needed.

(A mapping is not a specialized set, at least as far as typing is concerned: `isinstance({}, set)` is false. Semantically, they may be related, but the question here is whether the types are actually related.)
msg298335 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-07-14 04:13
Raymond, if you either unassign yourself or approve the current PR, I will merge and backport to 3.6 (but not 3.5).
History
Date User Action Args
2017-07-21 15:24:03rhettingersetassignee: rhettinger -> terry.reedy
2017-07-14 04:13:39terry.reedysetmessages: + msg298335
2017-07-14 01:13:23thomassensetmessages: + msg298327
2017-07-11 02:03:20terry.reedysetmessages: + msg298099
2017-06-30 22:14:48terry.reedysetnosy: + terry.reedy
messages: + msg297448
2017-06-30 11:19:46thomassensetmessages: + msg297391
2017-06-30 11:02:56python-devsetpull_requests: + pull_request2579
2017-06-29 20:19:15rhettingersetpriority: normal -> low

nosy: + rhettinger
messages: + msg297312

assignee: docs@python -> rhettinger
2017-06-29 13:36:18thomassencreate