PEP 285: Adding a bool type

Ype Kingma ykingma at accessforall.nl
Mon Apr 8 18:23:49 EDT 2002


Alex,

> > The language spec does not specify the type of the index or key
> > argument to __getitem__.
> 
> It does for sequences (int and slice only) and for dictionaries (any
> hashable) but not for other mappings (each mapping define its
> own rules: something like "only a string is accepted" is quite
> common in various mappings implemented in the standard
> library, for example).

I concluded that the type of the key argument is not specified from:
http://www.python.org/doc/current/ref/sequence-types.html :

__getitem__(self, key) 

 Called to implement evaluation of self[key]. For sequence types, the accepted keys
 should be integers and slice objects. 
 Note that the special interpretation of negative indexes (if the class wishes to
 emulate a sequence type) is up to the __getitem__() method. If key is of an
 inappropriate type, TypeError may be raised; if of a value outside the set of
 indexes for the sequence (after any special interpretation of negative values),
 IndexError should be raised. Note: for loops
 expect that an IndexError will be raised for illegal indexes to allow proper
 detection of the end of the sequence. 

End of quote.

 
> >>     - testing membership with aset.has_key(item),
> >> or:
> >>     - indicating membership with aset[item] = 1, then
> >>     - testing membership with aset.get(item, 0)
> >>
> >> Why would you think this is "rare"?  It's common currency.  How many tens
> >> of thousands of lines of python have you written, reviewed, or
> >> maintained, that you presume to teach me what is common and what is rare
> >> in python "as she is spoken", pray?
> >
> > In how many of these cases would the 'item' key actually be confused
> > when it could be either a bool or an int? So far you failed to convince me
> > that this is not a rare case.
> 
> What does it matter what type the item is?  has_key must now return

Because that is where the confusion would lead to problems when
True and 1 are considered different keys.

> a Boolean, while the get idiom that always was equivalent to it must
> keep returning int.  Any existing code that has tests coded both ways,
> for ANY type of item, will return "mixed" Booleans and int's.  Modern
> equivalent idiom "item in aset" also returns Boolean, but THAT is quite
> likely rare since it only became possible in Python 2.2.  Still, where 2.2
> was accepted as the used language version, whenever the get idiom
> was used I refactored it into the 'in' operator on code I was revising
> anyway (but not on parts of code I wasn't revising) -- clearer, more
> concise, AND faster... irresistible (the "one obvious way to do it", once
> it became acceptable).

Such mixing of bool and int would be handled in the special methods of
the bool and int classes, eg. the __add__ method of the bool class
would use the appropriate integer value when both operands are bools.
 
> > Even though I don't see the relevance of the second question
> > the answer is: a few, and I'm not teaching here, I'm explaining
> > my opinion.
> 
> Based on WHAT experience of Python, that's what I'm asking --
> maintenance responsibility for how many tens of thousands of lines,

A few.

> etc.  How can you have a sound opinion on what is rare or frequent if
> not on the basis of some reasonably representative sample?

The opinion comes from my intuition that most of the work on mixed bool
and other types would be done in a backwards compatible way by the special
methods of the standard types.

For example, assuming pure bool True and False:

True + True   --> 2   # done by the __add__ method bool
5 * False     --> 0   # by __mul__ of int, or by __rmul__ of bool
True - 1      --> 0   # by __sub__ of bool, or by __rsub__ of int.

And even:

'01'[True]    --> '1'   # done by __getitem__ of string.

As I said earlier, I don't like this behaviour, but I am
in favour of PEP 285, and I accept this.

However, dictionary indexing can be problematic as I indicated above.

For example when indicating membership with aset[item] = 1, 
this would break:

m1 = aset.has_key(item)
someDict[m1] = value1
m2 = aset.get(item, 0)
someDict[m2] = value2

To break, it not only needs _both_ styles of membership testing
to occur in the same program, but also the results of both 
styles should be used to index a dictionary.

How often are the direct results of both styles of tests used to
index a dictionary?

> >> > That should only happen when True and 1 are used interchangeably,
> >> > which might happen by programmer mistake or by using
> >> > old (1/0) and new (True/False) style code together.
> >>
> >> Old-style only will do perfectly well, since lots of extremely common
> >> idioms therein (a<b, d.has_key(k), x in y, and so on) change return
> >> values silently.  Neither any mistake nor any 'together' is needed,
> >> unless you're somehow trying to imply that anybody coding some kind
> >> of "advanced" (?!) construct such as "a<b" was using "new style code"
> >> unwittingly years ago...
> >
> > By new style I meant explicit use of True/False.
> 
> So do I: and there is absolutely no "explicit use of True/False" in any
> of the several expressions I enumerted, nor the many more whose
> return type change silently from int to Boolean.  "True and 1 are used
> interchangeably" in any existing function that can return any of these
> expressions OR an explicit 0 or 1, as in:

I meant this to be limited to the context of indexing dictionary
like objects. Somehow that context was lost :(

> def isValueAtIndex(alist, anindex, avalue):
>     if -len(alist)<=anindex<len(alist):
>         return alist[ainindex] == avalue
>     else:
>         return 0
> 
> or equivalently and more Pythonically:
> 
> def isValueAtIndex(alist, anindex, avalue):
>     try: return alist[ainindex] == avalue
>     except IndexError: return 0
> 
> There is absolutely no programmer mistake here, nor any use of old
> style and new style code together.  Yet such existing code may very
> well return int or Boolean interchangeably _because the return type
> of very common operations has changed silently in 2.3_ ... !!!

Which I expect to be handled well by other special methods, see above.

> > As for "advanced (?!): I don't have a time machine.
> 
> So you'll have to admit that claiming "interchangeable return of int
> or Boolean" can only be caused by mistakes or "mixing old and
> new style" was a wildly unfounded assertion, yes?

Again the context was lost.

> >> > In the pure case an int() call would not be needed at all:
> >>
> >> But to check whether it's needed or not, a type-check would be
> >> needed, and that IS a substantial slow-down.  You don't seem to
> >
> > If it's not needed at all, there is also no need for the check.
> 
> How do you *KNOW* whether to check or not, without a check?

Since bool would be normally hashable and there would be no
need to 'cast' True/False to 1/0, the __getitem__ method of dict
would not need to treat bool as a special case.

> > I am not talking about an actual implementation.
> > I'm talking about a specification, while trying to keep an eye on
> > implementABILITY.
> > Since you didn't say it cannot be done, I'm actually encouraged.
> 
> It IS quite possible to implement a language where the semantics
> of, say, + and - operators, are switched in any source line which has a
> prime number of characters.  It IS totally absurd, but clearly feasible.
>
> Why such technical feasibility would encourage anybody to consider
> such absurdities is beyond me, but please feel free to go ahead and
> implement such a beauty.  It will still be less absurd and abstruse
> than your cherished "implementable" specification, anyway.
> 

Most of the work to handle mixed bool and other types I meant to be done
in a backwards compatible way.

Regards,
Ype


P.S.

Deprecation of mixed type dictionary indexing could be done in phases:
a. deprecate indexing of standard dictionaries using both PEP 285 bool
   and int keys.
b. introduce pure bool and emulate the 1/0 behaviour of pure
   True and False. This takes the performance hit for the typecheck
   on bool and the cast to 1/0 for __getitem__ (and friends) of
   standard dicts.
c. end the deprecation by removing the this typecheck.

Phase b. would off course be as short as possible.

Some other interesting code I remember reading about would also
be involved in deprecation:

type(b) == type(1) # breaks in 2.3?, ie. already deprecated?

# breaks when removing the subtype relation between bool and int:
isinstance(b, type(1))


-- 
email at xs4all.nl



More information about the Python-list mailing list