PEP 285: Adding a bool type: yes, but not as int subtype

Bengt Richter bokr at oz.net
Sun Apr 7 23:48:09 EDT 2002


On Thu, 04 Apr 2002 05:46:03 GMT, Alex Martelli <aleax at aleax.it> wrote:

>Bengt Richter wrote:
>        ...
>>>in the C code (and I haven't checked), then __nonzero__() (or
>>>__len__()) is called if defined.  This gives us the control we're
>>>looking for.
>> 
>> Well, I wouldn't want to mess with __len__ if I e.g., were subtyping str
>
>Of course not.  But __nonzero__ exists for the SOLE purpose of
>defining the object's truth value.
>
I didn't know that was its SOLE purpose. Its name has nothing to do with
"truth value," so when I first saw it, I thought to myself that it was
something I'd have to look into if I defined a class that had some numeric
aspect to expose. I assumed it would be about numbers and/or perhaps indexing,
or sequence access control, etc., but certainly not that its ONLY purpose
was representing a custom boolean value.

IOW, I should have RTFM better, but I think TFM could have used less misleading
names, and I do think __nonzero__ would be better named __bool__. At least that
would have snagged my attention in a different way.

>> for something where I wanted to be able to write
>> 
>>     if s: print '"%s" (length %d) is special.' % (s, len(s))
>> 
>> I.e., in a logical context( if x..., x and ..., etc.), I think
>> x.__bool__() should have priority over x.__nonzero__() or x.__len__().
>
>__nonzero__ is used for nothing else except obtaining the object's
>truth value.  You don't appear to understand this...?
>
You're right. I didn't. The name totally fooled me when I first saw it.
I assumed that it had some unknown numeric use that might be broken if
I wrote a kludge to misuse it as a bool. At the time, my normal curiosity
was busy with something else, so I didn't look at the implementation
(or even the official doc ;-/) to check my assumption. I really should
have done better homework before talking about it, I grant you.

Now __nonzero__ is specified to return 0 or 1 as the "truth value."
I assume that will change with PEP 285? But whether True/False or 1/0, surely
__nonzero__ suggests that what is true or not is whether the thing is zero or not.

I think 'paper jammed' or 'connected' or 'tainted' etc. don't fit well with "nonzero".
That may seem a minor quibble, but ISTM, not unrelated to why Guido wants a bool
type in the first place.

>>>I certainly don't object to implementing the bool type.  I guess I
>>>just don't see it as a significant advantage.
>> 
>> I'd like the semantic clarity. And I'd like to define a class or subtype
>> whose boolean logical value doesn't depend on anyones concept of Nothing
>> or Empty if those don't fit. 
>
>You CAN do that, TODAY.  Therefore, your major motivation for
>desiring booleans seems to be an insufficient knowledge of Python/
>
I can do part, that's true. But I can't have the semantic clarity of returning
an official bool whose str value will be True or False, or something that I can
control through subtyping bool. And I can't do it through an interface whose name
reflects the general bool that it's delivering. I can build my own world that does
this, but that's not the same as using official infrastructure. So saying I CAN do
that TODAY is focusing on a narrow aspect.

I did mistake __nonzero__ for something I shouldn't use arbitrarily for my own purposes.
I thought from the name that __nonzero__ depended on someone's concept of zero and
nonzero, and I felt that returning a boolean through __nonzero__ would not be appropriate
unless my class had a state that could meaningfully be described as zero or non-zero.

That's why I was trying to propose something that ties in better with bool and bool(x).
What better than __bool__ ?

But thank you for pushing me to RTFM:
"""
__nonzero__(self) 
Called to implement truth value testing; should return 0 or 1.
When this method is not defined, __len__() is called, if it is
defined (see below). If a class defines neither __len__() nor __nonzero__(),
all its instances are considered true.  
"""

I am assuming, but haven't seen explicitly in the PEP, that bool(x) will result
in bool(x.__nonzero__()) if the __nonzero__ method is defined. Unfortunately
(ISTM), the code spec seems to preclude use of bool subtypes via x.__nonzero__().
IOW, str(bool(x)) will be limited to False/True, because bool(x) always returns
a singleton of the base class, never passing through possible compatible subtypes
(however unusual a constructor that might be ;-)

            def __new__(cls, val=0):
                      # This constructor always returns an existing instance
                      if val:
                          return True
                      else:
                          return False

I'd like to do something like the following (though this does not work, apparently
for several reasons, so please take it as pseudo-code to convey the idea):

            def __new__(cls, val=0):
                      # This constructor always returns an existing instance
                      try:
                          vbool = val.__nonzero__() # will crash if val is an integer[1]
                      except:
                          vbool = val
                      if isinstance(vbool, bool):
                          return vbool
                      elif vbool:
                          return True
                      else:
                          return False
 
[1] More directly illustrated by the following, which seems[2] like a bug:

 Python 2.2 (#28, Dec 21 2001, 12:21:22) [MSC 32 bit (Intel)] on win32
 Type "help", "copyright", "credits" or "license" for more information.
 >>> hasattr(1, '__nonzero__')
 1
 >>> 1 .__nonzero__()

(You get a memory fault).

[2] I now know it's a known bug, and fixed in cvs.

>> use when I poll. Or take a whimsical example: What if I want to subclass
>> int and be able to say if n: print n,'is a prime', expecting that
>> n.__bool__() will be called so I can calculate primeness and cache the
>> result in a boolean attribute for faster return next time, etc.
>
>You CAN do that, TODAY.  Whether it's wise or not is another issue,
>but, for example:
>
>class intWithPredicate(int):
>
>    def __init__(self, value=0, predicate=None):
>        int.__init__(self, value)
>        self.setPredicate(predicate)
>
>    def setPredicate(self, predicate=None):
>        self.predicate = predicate
>        self.isResultKnown = 0
>
>    def __nonzero__(self):
>        if self.isResultKnown: 
>            return self.result
>        if self.predicate is None:
>            self.result = self != 0
>        else:
>            self.result = self.predicate(self)
>        self.isResultKnown = 1
>        return self.result
>
The predicate generalization is a nice touch. And your example demonstrates
that I can do part of what I'd like now. Now can you also get the result to
show up when you print bool(x) and result is a subtype of bool
(which I don't think is possible with the current bool definiton, but that's
what I'd like). (It would be a strange constructor, but how else to get
a bool subtype from an object via its __nonzero__ method? It would be ugly to
write print bool(x) for ordinary True/False, and print y.__nonzero__() for
bool subtypes, and not be able to write [str(bool(x)) for x in objlist]
without losing custom bool strs.

>You don't need any change to Python to implement this functionality,
>whether it makes sense or not to have the functionality in question.
>
You captured a good part of what I'd like to do, but not all of it.
>
>> The point is to have freedom to use a useful general mechanism without
>
>The point is you HAVE the freedom, NOW -- the mechanism is
>general and there are no semantic contortions.
Well, stretching the meaning of nonzero to mean true is a semantic contortion, IMO ;-)

BTW, I think it might be helpful to mention in the PEP how bool(x) relates to __nonzero__
and __len__.  I expect that it will be ok for __nonzero__ to return 0/1 or False/True and
that all will be normalized to False/True in any case.

>
>> semantic contortions, or giving up the ability if __nonzero__ and/or
>> __len__ already have uses for the class.
>
>What "use for the class" do you expect __nonzero__ to have, as it
>is ONLY used to define the object's truth value?!
>
Well, I thought it might be used to indicate that there was something
non-zero about an object. I wasn't willing to assume that there was no
such independent number-specific use, given the suggestiveness (to me)
of the name. But now I know better ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list