Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implicit conversion to boolean in if and while statements

366 views
Skip to first unread message

Andrew Berg

unread,
Jul 15, 2012, 4:34:46 AM7/15/12
to comp.lang.python
This has probably been discussed before, but why is there an implicit
conversion to a boolean in if and while statements?

if not None:
print('hi')
prints 'hi' since bool(None) is False.

If this was discussed in a PEP, I would like a link to it. There are so
many PEPs, and I wouldn't know which ones to look through.

Converting 0 and 1 to False and True seems reasonable, but I don't see
the point in converting other arbitrary values.

--
CPython 3.3.0b1 | Windows NT 6.1.7601.17803

Chris Angelico

unread,
Jul 15, 2012, 4:47:05 AM7/15/12
to pytho...@python.org
On Sun, Jul 15, 2012 at 6:34 PM, Andrew Berg <bahamut...@gmail.com> wrote:
> Converting 0 and 1 to False and True seems reasonable, but I don't see
> the point in converting other arbitrary values.

It's for convenience. Unfortunately, not all languages treat all types
the same way. It's very handy, though, to be able to use

if not foo: foo = some_initializer

when foo starts out as, say, None. Or []. Or, in fact, any other "empty" value.

ChrisA

Stefan Behnel

unread,
Jul 15, 2012, 5:17:12 AM7/15/12
to pytho...@python.org
Andrew Berg, 15.07.2012 10:34:
> This has probably been discussed before, but why is there an implicit
> conversion to a boolean in if and while statements?

There isn't. This has nothing to do with "if" or "while".

All objects have a truth value in Python, evaluating to True by default
(object), unless they implement the test themselves.

As Chris said, very convenient.

Stefan

Steven D'Aprano

unread,
Jul 15, 2012, 6:56:57 AM7/15/12
to
On Sun, 15 Jul 2012 03:34:46 -0500, Andrew Berg wrote:

> This has probably been discussed before,

By the hoary hosts of Hoggoth, has it ever!

> but why is there an implicit
> conversion to a boolean in if and while statements?

It's nothing to do with if and while. All Python objects are duck-typed
as bools.

1) It's generally part of the duck-typing philosophy. If an object quacks
like a bool, why not treat it as a bool?

2) It's useful and convenient for short-circuit boolean expressions such
as any(), all(), and various things like:

for x in mylist or []:
...

is better than:

if mylist is not None:
for x in mylist:
...

3) Rather than distinguishing "true" from "false", a more useful
dichotomy is between "something" and "nothing". Python includes a number
of ways of spelling "nothing" of various types, such as:

None, 0, 0.0, '', [], {}, set()

and nearly everything else is "something".

4) Other languages such as Ruby, Javascript, PHP, Clojure and others also
distinguish between true-like and false-like ("truthy" and "falsey")
values. Although some of them have made some pretty weird and arbitrary
choices for what counts as true-like and false-like, without Python's
general principle that "nothing" values should be false.

(E.g. Javascript considers Boolean(false) to be a true value!!!)

5) Prior to Python 2.2, there was no bool type and no True and False
values. In fact, here is an impassioned plea from an educator begging
Guido not to introduce True and False to the language, because duck-typed
truthy/falsey values are *so much better*.

http://groups.google.com/group/comp.lang.python/msg/2de5e1c8384c0360?hl=en

Sadly, or happily, Python did grow True and False values, but the
fundamental distinction between something and nothing still exists.

(For the record, I can only think of one trap for the unwary: time
objects are false at *exactly* midnight.)


--
Steven

Ian Kelly

unread,
Jul 15, 2012, 12:19:16 PM7/15/12
to Python
On Sun, Jul 15, 2012 at 4:56 AM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> (For the record, I can only think of one trap for the unwary: time
> objects are false at *exactly* midnight.)

Ugh, that's irritating. I can't think of any scenario where I would
ever want the semantics "if timeval (is not midnight):". This
reinforces the point that if you only want to test whether you have
None, you should use "is not None" rather than relying on __bool__.

Rick Johnson

unread,
Jul 15, 2012, 12:50:05 PM7/15/12
to Python
I think this issue is not so much a "bool test" vs "type test", but more an ambiguous syntax issue. Consider this:

## EXAMPLE A ##
py> if money:
... do_something()

The syntax "if money" implies we are testing/measuring some attribute of "money", but what exactly about money are we testing/measuring? The problem lies in the syntax. To understand this syntax, we must first interpret what *IF* means, and we should *NEVER* need to interpret such a well defined word as *IF*! This syntax is far too opaque. Consider the alternative:

## EXAMPLE B ##
py> if bool(money):
... do_something()

Now we have a hint. Even if we don't understand the inner workings of the "bool" function, we *do* understand that the statement "bool(money)" *must* return True or the block *will not* execute.

We must NEVER present "if" in such confusing manner as ExampleA. I believe Guido made a grave mistake allowing this syntax to flourish. His intentions where noble, to save people a few keystrokes, but all he accomplished was to pave a road directly into hell.

"Explict is better than Implict"
Message has been deleted

Andrew Berg

unread,
Jul 15, 2012, 1:02:37 PM7/15/12
to comp.lang.python
On 7/15/2012 5:56 AM, Steven D'Aprano wrote:
> 3) Rather than distinguishing "true" from "false", a more useful
> dichotomy is between "something" and "nothing". Python includes a number
> of ways of spelling "nothing" of various types, such as:
>
> None, 0, 0.0, '', [], {}, set()
>
> and nearly everything else is "something".
Okay, I see the value in this, but I don't understand why None has a
truth value. I would expect None to mean "doesn't exist" or "unknown" or
something like that - e.g., a value of 0 means 0 jelly beans in the jar
and None means there isn't a jar.

FWIW, I have, for a reason I forget, gotten into the habit of writing
"if x is not None" when testing for None. However, I have not been
writing "if x is True: ..."/"elif x is False: ..."/"else: 'ruh-roh'"
when testing for True (in cases where a value of True or False makes
sense, but any other value would not). Should I?

Andrew Berg

unread,
Jul 15, 2012, 1:16:00 PM7/15/12
to comp.lang.python
On 7/15/2012 11:19 AM, Ian Kelly wrote:
> Ugh, that's irritating. I can't think of any scenario where I would
> ever want the semantics "if timeval (is not midnight):".
It's not implemented with such a test, but
logging.handlers.TimedRotatingFileHandler has an option to rollover at
midnight.

Ian Kelly

unread,
Jul 15, 2012, 1:45:10 PM7/15/12
to Python
On Sun, Jul 15, 2012 at 11:16 AM, Andrew Berg <bahamut...@gmail.com> wrote:
> On 7/15/2012 11:19 AM, Ian Kelly wrote:
>> Ugh, that's irritating. I can't think of any scenario where I would
>> ever want the semantics "if timeval (is not midnight):".
> It's not implemented with such a test, but
> logging.handlers.TimedRotatingFileHandler has an option to rollover at
> midnight.

Nor could it be implemented with such a test, since the rollover check
would then have to run at exactly midnight for the test to evaluate
false. If it were off by 1 microsecond, it would miss it.

Ian Kelly

unread,
Jul 15, 2012, 2:01:58 PM7/15/12
to pytho...@python.org
On Sun, Jul 15, 2012 at 10:50 AM, Rick Johnson
<rantingri...@gmail.com> wrote:
> I think this issue is not so much a "bool test" vs "type test", but more an ambiguous syntax issue. Consider this:
>
> ## EXAMPLE A ##
> py> if money:
> ... do_something()
>
> The syntax "if money" implies we are testing/measuring some attribute of "money", but what exactly about money are we testing/measuring? The problem lies in the syntax. To understand this syntax, we must first interpret what *IF* means, and we should *NEVER* need to interpret such a well defined word as *IF*! This syntax is far too opaque. Consider the alternative:
>
> ## EXAMPLE B ##
> py> if bool(money):
> ... do_something()
>
> Now we have a hint. Even if we don't understand the inner workings of the "bool" function, we *do* understand that the statement "bool(money)" *must* return True or the block *will not* execute.

So now instead of having to understand how "if" handles arbitrary
values, we have to understand how "bool" handles arbitrary values.
How is that an improvement?

What should "if" do if presented a value that isn't True or False?
Raise a TypeError?

Next thing we know, people get so used to wrapping everything they
present to "if" in a "bool()" call, that they start writing silly
things like "if bool(x == 7)" and "if bool(isinstance(x, int))". Why?
Because it's faster and easier to automatically wrap the value in
"bool" than it is to put in the effort to verify that the value will
always be a bool to begin with in order to avoid a useless and
annoying exception. At the point that happens, the "bool()" is
effectively just part of the if syntax, and we're back to where we
started.

Rick Johnson

unread,
Jul 15, 2012, 2:56:49 PM7/15/12
to pytho...@python.org
On Sunday, July 15, 2012 1:01:58 PM UTC-5, Ian wrote:

> So now instead of having to understand how "if" handles arbitrary
> values, we have to understand how "bool" handles arbitrary values.
> How is that an improvement?

Because we are keeping the condition consistent. We are not relying on implicit resolution of an object's value based on some dark, esoteric and inconsistent rules that defy all normal logic.

> What should "if" do if presented a value that isn't True or False?
> Raise a TypeError?

YES! Because IT IS the author's responsibility to present a condition that evaluates to either True or False. Anything else would be ridiculously inconsistent.

> Next thing we know, people get so used to wrapping everything they
> present to "if" in a "bool()" call, that they start writing silly
> things like "if bool(x == 7)" and "if bool(isinstance(x, int))".

We cannot prevent morons from doing stupid things. "x==7" IS an explicit statement that evaluates to either True or False. Likewise, isinstance(obj, type) is a function that evaluates to either True or False. Wrapping either example in a bool call is redundant and only obfuscates the meaning. True equals True and False equal False. Why do you need to test that truth?

The only time you will be forced to use the bool is when you are NOT using rich comparisons or NOT using truth testing functions in a condition. The following require NO bool function:

obj == obj -> bool
obj != obj -> bool
obj > obj -> bool
obj < obj -> bool
obj >= obj -> bool
obj <= obj -> bool
isinstance(obj, type) -> bool
callable(obj) -> bool
hasattr(obj, name) -> bool
issubclass(obj, name) -> bool
..along with any function that returns a bool

Whereas:
"if obj" -> Some esoteric semantics that defies all logic!

> Why?
> Because it's faster and easier to automatically wrap the value in
> "bool" than it is to put in the effort to verify that the value will
> always be a bool to begin with in order to avoid a useless and
> annoying exception.

No, because we want our code to be EXPLICIT and consistent! Remember, writing obfuscated code is easy, however, interpreting obfuscated code is difficult! A good measure of your programming skill is to see how easily your code is read by the majority.

"""If it's difficult to explain, it's probably a bad idea""".

"if blah" is difficult to explain, whereas "if bool(blah)" is not.

> At the point that happens, the "bool()" is
> effectively just part of the if syntax, and we're back to where we
> started.

That's a ridiculous conclusion. See points above^^^

Message has been deleted

Terry Reedy

unread,
Jul 15, 2012, 4:09:44 PM7/15/12
to pytho...@python.org
On 7/15/2012 12:19 PM, Ian Kelly wrote:
> On Sun, Jul 15, 2012 at 4:56 AM, Steven D'Aprano
> <steve+comp....@pearwood.info> wrote:
>> (For the record, I can only think of one trap for the unwary: time
>> objects are false at *exactly* midnight.)
>
> Ugh, that's irritating. I can't think of any scenario where I would
> ever want the semantics "if timeval (is not midnight):". This

When printing time tables, midnight may be either 24:00 or 0:00,
depending on whether it is the end or start of a journey. That could, of
course, be done by explicit if time == midnight: rather than if not time:.

Whether to change the current behavior was discussed on python-ideas a
couple of months ago. I believe inertia and back-compatibity and the
rare use case won.

> reinforces the point that if you only want to test whether you have
> None, you should use "is not None" rather than relying on __bool__.

Right.

--
Terry Jan Reedy



Terry Reedy

unread,
Jul 15, 2012, 4:28:02 PM7/15/12
to pytho...@python.org
On 7/15/2012 1:02 PM, Andrew Berg wrote:
> On 7/15/2012 5:56 AM, Steven D'Aprano wrote:
>> 3) Rather than distinguishing "true" from "false", a more useful
>> dichotomy is between "something" and "nothing". Python includes a number
>> of ways of spelling "nothing" of various types, such as:
>>
>> None, 0, 0.0, '', [], {}, set()
>>
>> and nearly everything else is "something".
> Okay, I see the value in this, but I don't understand why None has a
> truth value.

Because everything does (or should).

> I would expect None to mean "doesn't exist" or "unknown" or
> something like that - e.g., a value of 0 means 0 jelly beans in the jar
> and None means there isn't a jar.
>
> FWIW, I have, for a reason I forget, gotten into the habit of writing
> "if x is not None" when testing for None.

If x might possibly be any other false value (as is, I think, the usual
case), that is the right thing to do. Even if no other false value is
possible, I would still use 'is not None' just to be clear what the
false alternative is, and to guard against mistakes (like not knowing
that time values can be false) or code changes that add the possibility
of ohter false values.

> However, I have not been
> writing "if x is True: ..."/"elif x is False: ..."/"else: 'ruh-roh'"
> when testing for True (in cases where a value of True or False makes
> sense, but any other value would not). Should I?

If you only want to execute the if branch when x is literally the bool
object True and x could be some other non-bool true value such as 1 or
'a' or [1], etc, then you should.

If x is guaranteed to be True or False, which is the case you more or
less proposed, then you should not. For instance, "if isinstance(x, int)
is True:" or "if (n > 3) is True:" are redundant.

--
Terry Jan Reedy



Chris Angelico

unread,
Jul 15, 2012, 5:53:09 PM7/15/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 4:56 AM, Rick Johnson
<rantingri...@gmail.com> wrote:
> On Sunday, July 15, 2012 1:01:58 PM UTC-5, Ian wrote:
>
>> So now instead of having to understand how "if" handles arbitrary
>> values, we have to understand how "bool" handles arbitrary values.
>> How is that an improvement?
>
> Because we are keeping the condition consistent. We are not relying on implicit resolution of an object's value based on some dark, esoteric and inconsistent rules that defy all normal logic.
>
>> What should "if" do if presented a value that isn't True or False?
>> Raise a TypeError?
>
> YES! Because IT IS the author's responsibility to present a condition that evaluates to either True or False. Anything else would be ridiculously inconsistent.

Then the construct "if bool(some_condition):" is redundant. What
you've described is a viable system (REXX, for instance, demands that
an IF statement be given strictly either a 1 or a 0 (there's no True
and False values, but you're not allowed to use 527 as a True value,
it has to be 1)), but it's still redundant to attempt the cast. For
instance, this REXX function will logically negate a value twice, thus
forcing it to boolean:

bool: procedure
return \\arg(1)

But if it's given a non-bool, it'll just bomb, exactly the same as the
if statement would. So Ian's point still stands.

ChrisA

Ranting Rick

unread,
Jul 15, 2012, 9:21:06 PM7/15/12
to
On Jul 15, 4:53 pm, Chris Angelico <ros...@gmail.com> wrote:
> Then the construct "if bool(some_condition):" is redundant.

Wrong again, pay attention Chris!

It's ONLY redundant IF "some_condition" is a rich comparison: like
"(a==b)" OR a boolean function: like "callable(a)".

If HOWEVER we want to "truth test" an object (as in: "if obj") we
should be FORCED to use the bool! Why? Because explicit is better than
implicit and readability counts if we want to create maintainable code
bases!

if bool(obj) and a==b: # Correct!
if obj and a==b: # Incorrect!

Both lines of code currently produce the same result because
"somebody" decided to give objects esoteric boolean values. Sure, we
saved a few key stokes in a condition, but sadly at the cost of
readability and consistency. I see no reason why choosing implicit
resolution is better than explicit resolution. Saving six keystrokes
is simply not enough!

Python's motto has always been "readability counts", and for that
reason, we should return to Explicit Boolean Resolution if we want to
adhere to those principals.

Chris Angelico

unread,
Jul 15, 2012, 9:51:13 PM7/15/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 11:21 AM, Ranting Rick
<rantingri...@gmail.com> wrote:
> If HOWEVER we want to "truth test" an object (as in: "if obj") we
> should be FORCED to use the bool! Why? Because explicit is better than
> implicit and readability counts if we want to create maintainable code
> bases!
>
> if bool(obj) and a==b: # Correct!
> if obj and a==b: # Incorrect!

That still doesn't answer the question of what bool(obj) should do if
obj is not a bool, and why if can't do the exact same thing, since if,
by definition, is looking for a boolean state selector.

ChrisA

Steven D'Aprano

unread,
Jul 15, 2012, 10:13:56 PM7/15/12
to
On Sun, 15 Jul 2012 10:19:16 -0600, Ian Kelly wrote:

> On Sun, Jul 15, 2012 at 4:56 AM, Steven D'Aprano
> <steve+comp....@pearwood.info> wrote:
>> (For the record, I can only think of one trap for the unwary: time
>> objects are false at *exactly* midnight.)
>
> Ugh, that's irritating. I can't think of any scenario where I would
> ever want the semantics "if timeval (is not midnight):".

Yes, it is a genuine gotcha. Time values are numbers, and zero is falsey,
so midnight is falsey even though it shouldn't be.

There's no good solution here, since we have a conflict between treating
time values as time values ("midnight is nothing special") and as numbers
("midnight == 0 which is falsey"). The only "solution" is to throw out
duck-typing of boolean values, which is tossing the baby out with the
bathwater -- bool duck-typing works fine for everything else.


> This
> reinforces the point that if you only want to test whether you have
> None, you should use "is not None" rather than relying on __bool__.

Often you should, but I didn't mention anything about testing for None.
Testing objects in a boolean context is more than just testing for None.

I have just written a bunch of code with about two dozen examples similar
to this:

for item in (seq or []):
do_something_with(item)

iterates over seq if it is non-empty, or the empty list. Writing it like
this would be more painful, more complex, less readable and less
idiomatic:

if seq is not None:
for item in seq:
do_something_with(item)


not to mention completely unnecessary if you have already checked that
seq is either None or a sequence, and not some other arbitrary value.

(If seq really could be any type at all, then an explicit identity test
against None is required.)


One of my favourites:

value = (dict or {}).get('key')

instead of:

value = None if dict is None else dict.get('key')



--
Steven

Devin Jeanpierre

unread,
Jul 15, 2012, 10:15:13 PM7/15/12
to Chris Angelico, pytho...@python.org
On Sun, Jul 15, 2012 at 9:51 PM, Chris Angelico <ros...@gmail.com> wrote:
>> if bool(obj) and a==b: # Correct!
>> if obj and a==b: # Incorrect!
>
> That still doesn't answer the question of what bool(obj) should do if
> obj is not a bool, and why if can't do the exact same thing, since if,
> by definition, is looking for a boolean state selector.

If can obviously do the exact same thing -- it does, in Python.

I don't agree with the angle that Rick is spinning, so let me write my
own: By forcing the objects in conditional to be booleans, you are
forced to do something to non-booleans to convert them. By doing so,
you will help inform the reader what the non-boolean is, which makes
it easier for them to figure out the code.

For example, instead of "if stack:" or "if bool(stack):", we could use
"if stack.isempty():". This line tells us explicitly that stack is a
container. Or instead of "if dist:" or "if bool(dist):" we could use
"if dist == 0:". This tells us explicitly that stack is a number.
Supposedly this makes it easier to read code. It certainly reads more
like English! :)

As far as I know, the only use of having a polymorphic boolean
conversion is reducing the amount of typing we do. Generally objects
with otherwise different interfaces are not interchangeable just
because they can be converted to booleans, so you wouldn't lose much
by being forced to explicitly convert to boolean with something
interface-specific.

-- Devin

Ranting Rick

unread,
Jul 15, 2012, 10:31:23 PM7/15/12
to
On Jul 15, 8:51 pm, Chris Angelico <ros...@gmail.com> wrote:
> On Mon, Jul 16, 2012 at 11:21 AM, Ranting Rick
>
My point is no different than this example:

py> cost = 1.75
py> cost
1.75
py> 'Cost = ' + cost

Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
'Cost = ' + cost
TypeError: cannot concatenate 'str' and 'float' objects
py> 'Cost = ' + str(cost)
'Cost = 1.75'

We DON'T want Python to silently convert "cost" to a string. What we
DO want is to force the author to use the str function thereby making
the conversion explicit.

Same with converting objects to bools.

We DON'T want "if" to magically convert a non-boolean into a boolean.
What we DO want is to force the author to use the bool function
thereby making the conversion explicit. By doing so we transform
confusion into comprehension. By doing so we maintain the principals
of readability counts.

Steven D'Aprano

unread,
Jul 15, 2012, 10:38:35 PM7/15/12
to
On Sun, 15 Jul 2012 12:02:37 -0500, Andrew Berg wrote:

> On 7/15/2012 5:56 AM, Steven D'Aprano wrote:
>> 3) Rather than distinguishing "true" from "false", a more useful
>> dichotomy is between "something" and "nothing". Python includes a
>> number of ways of spelling "nothing" of various types, such as:
>>
>> None, 0, 0.0, '', [], {}, set()
>>
>> and nearly everything else is "something".
> Okay, I see the value in this, but I don't understand why None has a
> truth value.

And this is exactly the sort of mental confusion that Laura Crichton
warned about (see the link I included earlier).

Thinking about "truth values" is harmful, since that's arbitrary. That
way goes to Javascript, PHP, Ruby etc. that seem to arbitrary pick
whatever things are truthy or falsey according to some random whim, or
according to some implementation detail that is meaningless outside of
the implementation, such as Javascript insisting that while false is
falsey, if you box it in an object it becomes truthy.

It's crap like that which gives duck-typing bools a bad name.

The distinction you should consider is:

- is it something, or nothing?

(relative to the type in question, of course)

Python (at least the built-ins, third-party code can do any old crap they
want) is consistent in this. Instances which represent something/non-
emptiness are true, those which represent nothing/emptiness are false.

0? That's the int that represents nothing, so it's false.

23.723? That's one of many floats that represents something, so it's true.

'spam'? That's one of many non-empty strings, so it's true.

''? That's an empty string, that it, it contains nothing, so it is false.

None? That represents a lack of a thing, that is, nothing, so it's false.

(Please don't get into a great philosophical debate over whether
nothingness is itself something. That impressed me when I was 15. But now
I know about reification: just because we have a name for a concept
doesn't mean that the concept is something concrete. None is an object,
but it *represents* the lack of an object.)


> I would expect None to mean "doesn't exist" or "unknown" or
> something like that - e.g., a value of 0 means 0 jelly beans in the jar
> and None means there isn't a jar.

How you interpret some_variable = None depends on what some_variable
represents. If some_variable represents "number of jelly beans in a jar",
then that should be 0 if there is no jar.

If you want to create a language with ternary truth values (yes, no, mu)
or some larger number (yes, no, maybe, mu, contradiction, unknowable,
...) be my guest. Just do everyone a favour and do some research on the
large literature on non-boolean logic systems first.


> FWIW, I have, for a reason I forget, gotten into the habit of writing
> "if x is not None" when testing for None. However, I have not been
> writing "if x is True: ..."/"elif x is False: ..."/"else: 'ruh-roh'"
> when testing for True (in cases where a value of True or False makes
> sense, but any other value would not). Should I?

Only if you want people to laugh at you.

If you *genuinely* want to implement Java in Python, then be explicit
about your type-testing:

if isinstance(x, bool) and x: ...

or even

if type(x) is bool and x: ... # disallow subclasses

Otherwise, where do you stop?

if x is True is True is True is True is True is ...


Or you could just write idiomatic Python code, including duck-typing, and
that includes duck-typing bools. Why do you care if somebody calls your
function with flag=1 instead of flag=True?



--
Steven

Ranting Rick

unread,
Jul 15, 2012, 10:41:34 PM7/15/12
to
On Jul 15, 9:13 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:

> I have just written a bunch of code with about two dozen examples similar
> to this:
>
> for item in (seq or []):
>     do_something_with(item)
>
> iterates over seq if it is non-empty, or the empty list. Writing it like
> this would be more painful, more complex, less readable and less
> idiomatic:
>
> if seq is not None:
>     for item in seq:
>         do_something_with(item)
>
> not to mention completely unnecessary if you have already checked that
> seq is either None or a sequence, and not some other arbitrary value.

Short circuitry is a powerful tool! But why the heck would your
sequences ever be None? Are you using None as a default? And if so,
why not use an empty sequence instead ([], {}, "")?

Ranting Rick

unread,
Jul 15, 2012, 10:58:20 PM7/15/12
to
On Jul 15, 9:15 pm, Devin Jeanpierre <jeanpierr...@gmail.com> wrote:

> For example, instead of "if stack:" or "if bool(stack):", we could use
> "if stack.isempty():". This line tells us explicitly that stack is a
> container. Or instead of "if dist:" or "if bool(dist):" we could use
> "if dist == 0:". This tells us explicitly that stack is a number.
> Supposedly this makes it easier to read code. It certainly reads more
> like English! :)

Yes, but this approach involves adding new "value testing" methods to
every object.

Whilst these specific methods would probably inject more comprehension
than using bool, i believe the bool function can handle this problem
better due to its monolithic and generic nature. No need to memorize
which method is needed for strings, or integers, or lists, etc... just
use bool and everything works. As for the semantics, we should let the
object decide how to respond to a __bool__() request.

But what's the point of having a bool function if we refuse to use it
correctly? We force str, int, and float conversion all day, but not
the bool? Where is the consistency? Where is the bool!?

Steven D'Aprano

unread,
Jul 15, 2012, 10:58:44 PM7/15/12
to
On Sun, 15 Jul 2012 18:21:06 -0700, Ranting Rick wrote:

> If HOWEVER we want to "truth test" an object (as in: "if obj") we should
> be FORCED to use the bool! Why? Because explicit is better than implicit

And this is why Rick always writes code like:

integer_value_three = int(1) + int(2)
assert (int(integer_value_three) == \
int(3) is True) is True, str("arithmetic failed")
list_containing_three_values_which_are_all_integers_but_might_later_have_more_or_fewer_values_or_other_types = list([1, 2, integer_value_three])

because you can never have too much explicitness. Who wouldn't want
to read code like that?


> and readability counts if we want to create maintainable code bases!

Yes you, Rick, are correct, that is to say not wrong, that readability,
that is to say the quality of ease of reading the text in question,
counts, that is to say that it matters to people who care about ease of
reading, when our motivation is to create, that is to say write,
maintainable code bases, that is to say unified collections of code which
can have software errors fixed and new features added with relatively
small amounts of effort on behalf of the human programmer.

And that, the reason given in the sentence above, is the reason that we,
collectively all programmers, should prefer to be explicit, not merely
conveying meaning by implication about everything we, collectively all
programmers, write, including typing, use of speech-recognition software,
or any future technological process by which text or program code or both
is transcribed from the idea of the human person to a permanent form
recorded where other people, or non-human sentient beings, can read or
otherwise gain access to it for the purpose of understanding the content
of the test or program code or both.


--
Steven

Chris Angelico

unread,
Jul 15, 2012, 10:58:40 PM7/15/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 12:41 PM, Ranting Rick
<rantingri...@gmail.com> wrote:
> On Jul 15, 9:13 pm, Steven D'Aprano <steve
> +comp.lang.pyt...@pearwood.info> wrote:
>
>> I have just written a bunch of code with about two dozen examples similar
>> to this:
>>
>> for item in (seq or []):
>> do_something_with(item)
>
> Short circuitry is a powerful tool! But why the heck would your
> sequences ever be None? Are you using None as a default? And if so,
> why not use an empty sequence instead ([], {}, "")?

Function default arguments spring to mind, especially if the list will
be mutated afterwards.

ChrisA

Chris Angelico

unread,
Jul 15, 2012, 11:05:48 PM7/15/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 12:58 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> And that, the reason given in the sentence above, is the reason that we,
> collectively all programmers, should prefer to be explicit, not merely
> conveying meaning by implication about everything we, collectively all
> programmers, write, including typing, use of speech-recognition software,
> or any future technological process by which text or program code or both
> is transcribed from the idea of the human person to a permanent form
> recorded where other people, or non-human sentient beings, can read or
> otherwise gain access to it for the purpose of understanding the content
> of the test or program code or both.

I'd rather be booled in oil.

ChrisA
*ducks for cover*

Ranting Rick

unread,
Jul 15, 2012, 11:21:41 PM7/15/12
to
On Jul 15, 9:58 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Sun, 15 Jul 2012 18:21:06 -0700, Ranting Rick wrote:
> > If HOWEVER we want to "truth test" an object (as in: "if obj") we should
> > be FORCED to use the bool! Why? Because explicit is better than implicit
>
> And this is why Rick always writes code like:
...

Traceback (most recent quip last):
Author: "<DeAprano>", line 7, in <post>
LogicalFallacyError: "Reductio ad absurdum"

Steven D'Aprano

unread,
Jul 16, 2012, 12:03:29 AM7/16/12
to
On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:

> For example, instead of "if stack:" or "if bool(stack):", we could use
> "if stack.isempty():". This line tells us explicitly that stack is a
> container.

isempty is not a container method.

py> container = []
py> container.isempty()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'isempty'

Your code tells us explicitly that stack is expected to be an object with
an isempty() method. What does that mean? Who knows?

calories = macdonalds.fries('large')
calories.isempty()
=> returns True



When you want to write polymorphic code to handle your stack, you end up
doing something like this:

if isinstance(stack, MyStackClass):
flag = stack.isempty()
else:
try:
# list, collections.deque, many others
flag = len(stack) == 0
except AttributeError:
try:
if sys.version < '3':
flag = not stack.__nonzero__()
else:
flag = not stack.__bool__()
except AttributeError:
# Is this even possible in Python 3?
flag = False # I guess...
# If we get here, flag is true if stack is empty.
if flag:
...

Yeah, explicit is *so much better* for readability. Can't you just *feel*
how much more readable all those irrelevant implementation details are?

If you're smart, you wrap all of the above in a function:

def isempty(stack):
# blah blah as above


But if you're *really* smart, you write to the interface and let Python
take care of the polymorphic details for you:

if not stack:
...


(Assuming that stack defines __nonzero__ or __len__ correctly, which it
better if it claims to be a container.)

It boggles my mind that people who are perfectly happy to program to an
interface or protocol when it comes to (say) iterables, numbers or even
big complex classes with dozens of methods, suddenly freak out at the
thought that you can say "if obj" and obj is duck-typed.

There's a distinct lack of concrete, actual problems from duck-typing
bools, and a heavy over-abundance of strongly-held opinion that such a
thing is self-evidently wrong.


> As far as I know, the only use of having a polymorphic boolean
> conversion is reducing the amount of typing we do.

The same could be said about *every* polymorphic function.

The benefit is not just because you don't wear out your keyboard as fast.
The benefit is the same for all other polymorphic code: it lets you write
better code faster with fewer bugs and less need for unnecessary type
restrictions.

If there are a few corner cases where you actually *need* to restrict the
type of your flags to a actual bool, well, Python gives you the tools to
do so. Just as you can restrict the type of a sequence to exactly a list
and nothing else, or a number as exactly a float and nothing else. Just
do your type tests before you start operating on the object, and reject
anything that doesn't match what you want.

But that should be the exception, not the rule.


> Generally objects
> with otherwise different interfaces are not interchangeable just because
> they can be converted to booleans, so you wouldn't lose much by being
> forced to explicitly convert to boolean with something
> interface-specific.

Until somebody writes an awesomely fast stack class in C and gives it an
is_empty() method instead of isempty, and your code can't use it because
you made unnecessary assumptions about the implementation.



--
Steven

Steven D'Aprano

unread,
Jul 16, 2012, 12:20:40 AM7/16/12
to
Deary deary me Rick. Reductio ad adsurdum is not a fallacy. It is a
counter-argument to an argument or claim, by showing that the premise of
the original claim leads to an absurd conclusion.

You have claimed that we should always be explicit whenever we write. But
you do not actually live up to your own advice, because you can't: it is
absurd to try to be explicit about everything all the time. You have
misunderstood the purpose of the Zen of Python: it is not to claim that
everything should be explicit, but to avoid code that is hard to
understand because things which need to be explicit for clarity are
implied by other parts of your code.

(It's not like explicit and implicit are distinct -- everything depends
on something implicit, if only the meaning of the words you use to
describe it.)

It certainly doesn't mean that the semantics of Python the language must
be written out explicitly every time you use each feature.

for x in sequence: # Yes, implies that we iterate over the values

for loop variable named x in iterable sequence iterate over values and
assign the loop variable each time you go around the loop executing the
following block each time:
# No, since that tells us what we already know and just adds
# meaningless verbosity for the sake of faux "explicitness"



--
Steven

Ranting Rick

unread,
Jul 16, 2012, 12:53:51 AM7/16/12
to
On Jul 15, 11:03 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:

> It boggles my mind that people who are perfectly happy to program to an
> interface or protocol when it comes to (say) iterables, numbers or even
> big complex classes with dozens of methods, suddenly freak out at the
> thought that you can say "if obj" and obj is duck-typed.

"if obj" is in essence doing "if bool(obj)" behind the scenes. My
question is: Why hide such valuable information from the reader? It's
obvious that "if bool(obj)" will return a boolean; whereas "if obj" is
ambiguous.

> There's a distinct lack of concrete, actual problems from duck-typing
> bools, and a heavy over-abundance of strongly-held opinion that such a
> thing is self-evidently wrong.

If the multitudes of misunderstandings from "if obj" on this list have
not convinced you yet, then i lack the energy to educate you!

> > As far as I know, the only use of having a polymorphic boolean
> > conversion is reducing the amount of typing we do.
>
> The same could be said about *every* polymorphic function.

For which "bool" IS!

Wikipedia to the rescue:
"""In computer science, polymorphism is a programming language feature
that allows values of different data types to be handled using a
uniform interface. The concept of parametric polymorphism applies to
both data types and functions. A function that can evaluate to or be
applied to values of different types is known as a polymorphic
function."""

bool("a") -> True
bool(0) -> False
bool([1,2,3]) -> True
bool(True) -> True

> The benefit is not just because you don't wear out your keyboard as fast.
> The benefit is the same for all other polymorphic code: it lets you write
> better code faster with fewer bugs and less need for unnecessary type
> restrictions.

There are NO type restrictions for bool.

> If there are a few corner cases where you actually *need* to restrict the
> type of your flags to a actual bool, well, Python gives you the tools to
> do so.

Yes, the bool()

Ranting Rick

unread,
Jul 16, 2012, 1:03:52 AM7/16/12
to
On Jul 15, 11:20 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:

> (It's not like explicit and implicit are distinct -- everything depends
> on something implicit, if only the meaning of the words you use to
> describe it.)
>
> It certainly doesn't mean that the semantics of Python the language must
> be written out explicitly every time you use each feature.

Of course not. Don't be ridiculous.

> for x in sequence: [...]

This syntax is explicit *enough*. We don't need to be any more
explicit.

But if you are going to argue that "if obj" is *explicit enough*, then
apply your argument consistently to "String"+1.75 also. Why must we be
explicit about string conversion BUT not boolean conversion? Can you
reduce this to the absurd? Or will you just choose to ignore this
valid point?

Chris Angelico

unread,
Jul 16, 2012, 1:57:39 AM7/16/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 2:53 PM, Ranting Rick
<rantingri...@gmail.com> wrote:
> "if obj" is in essence doing "if bool(obj)" behind the scenes. My
> question is: Why hide such valuable information from the reader? It's
> obvious that "if bool(obj)" will return a boolean; whereas "if obj" is
> ambiguous.

Proves nothing. At least when there are less names used, there's less
possibility of monkey-patching.

>>> def bool(n):
... return 5
...
>>> if bool([]):
... print("Yay?")
...
Yay?

ChrisA

Chris Angelico

unread,
Jul 16, 2012, 2:00:06 AM7/16/12
to pytho...@python.org
On Mon, Jul 16, 2012 at 3:03 PM, Ranting Rick
<rantingri...@gmail.com> wrote:
> But if you are going to argue that "if obj" is *explicit enough*, then
> apply your argument consistently to "String"+1.75 also. Why must we be
> explicit about string conversion BUT not boolean conversion? Can you
> reduce this to the absurd? Or will you just choose to ignore this
> valid point?

Personally, I'm quite okay with automatic upcasts to string. But if
you want to be explicit, particularly with floats, the solution often
is not to use str(), but a proper number-formatting routine. You want
"String%f"%1.75 for full power.

But when you're just letting the language do the translation, it's
much of a muchness whether you put an explicit toString() call in.

ChrisA

Mark Lawrence

unread,
Jul 16, 2012, 2:30:14 AM7/16/12
to pytho...@python.org
On 16/07/2012 04:05, Chris Angelico wrote:
> On Mon, Jul 16, 2012 at 12:58 PM, Steven D'Aprano
> <steve+comp....@pearwood.info> wrote:
>> And that, the reason given in the sentence above, is the reason that we,
>> collectively all programmers, should prefer to be explicit, not merely
>> conveying meaning by implication about everything we, collectively all
>> programmers, write, including typing, use of speech-recognition software,
>> or any future technological process by which text or program code or both
>> is transcribed from the idea of the human person to a permanent form
>> recorded where other people, or non-human sentient beings, can read or
>> otherwise gain access to it for the purpose of understanding the content
>> of the test or program code or both.
>
> I'd rather be booled in oil.
>
> ChrisA
> *ducks for cover*
>

What a silly bunt[1] :)
*also ducks for cover*

[1] from a Monty Python sketch for those who don't know about a guy who
pronounces c's as b's, hence Kings Bollege Bambridge

--
Cheers.

Mark Lawrence.



alex23

unread,
Jul 16, 2012, 3:21:12 AM7/16/12
to
On Jul 16, 2:53 pm, Ranting Rick <rantingrickjohn...@gmail.com> wrote:
> "if obj" is in essence doing "if bool(obj)" behind the scenes. My
> question is: Why hide such valuable information from the reader?

If @decorator is in essence doing "function = decorator(function)"
behind the scenes, why hide that?
If classes are just syntactic sugar for dicts of functions, why hide
that?

> It's
> obvious that "if bool(obj)" will return a boolean; whereas "if obj" is
> ambiguous.

It's only ambiguous if you're the sort of idiot who believes every
language should conform to their a priori expectations. The behaviour
is _documented and standard_, so it's in no way ambiguous unless
someone has actively failed to do _their_ part and _educate
themselves_:

http://docs.python.org/library/stdtypes.html#truth-value-testing

> If the multitudes of misunderstandings from "if obj" on this list have
> not convinced you yet

By that argument, _every_ Python feature that is misunderstood should
be modified to meet the expectations of those who have refused to
understand the language. In which case, why even bother having
different languages with different ways of doing...oh wait, I'm
talking to the monoculture king. Forget that line of reasoning.

Also: citation or STFU. Provide links to 3 threads from the last month
that have revolved around misunderstanding of truth values.

> > > As far as I know, the only use of having a polymorphic boolean
> > > conversion is reducing the amount of typing we do.
>
> > The same could be said about *every* polymorphic function.
>
> For which "bool" IS!

Yes. That was Steven's point, which you missed in all of your usual
mouth frothing.

alex23

unread,
Jul 16, 2012, 3:27:18 AM7/16/12
to
On Jul 16, 3:03 pm, Ranting Rick <rantingrickjohn...@gmail.com> wrote:
> But if you are going to argue that "if obj" is *explicit enough*, then
> apply your argument consistently to "String"+1.75 also. Why must we be
> explicit about string conversion BUT not boolean conversion?

What _other_ than booleans can you expect a condition to reduce down
to? Seriously. What might you possibly expect 'obj' in 'if obj' to be?
Tuesday? The colour mauve? That sinking feeling that you're entering
into a debate that's far above your ability to understand it?

Now: as an expression "String"+1.75 can be _anywhere_, so _what_ you
want will very much be contextual. Do you want "String1.75"? Do you
want float("String") + 1.75? Do you want it to error? So yes, here you
very much need to be explicit.

> Can you reduce this to the absurd?

You've already taken care of that.

Steven D'Aprano

unread,
Jul 16, 2012, 3:52:04 AM7/16/12
to
On Sun, 15 Jul 2012 22:03:52 -0700, Ranting Rick wrote:

> But if you are going to argue that "if obj" is *explicit enough*, then
> apply your argument consistently to "String"+1.75 also. Why must we be
> explicit about string conversion BUT not boolean conversion?

The problem with "String" + 1.75 is not lack of explicitness, but
ambiguity. The + is operator is plenty explicit, but it is ambiguous when
the operands have different types. Should it...?

- truncate "String" at the first non-digit (hence "") and then coerce
it to 0.0, and hence return the float 1.75?

- coerce "String" to a float NaN on the basis that "String" is
not a number, and hence return NaN?

- coerce 1.75 to a string, and hence return "String1.75"?


The first behaviour is rather dubious, but a good case could be made for
the second or third. Python takes a fourth approach, and refuses to allow
such mixed type operations.

If + always meant "numeric addition", and & was used for string
concatenation, then we could have unambiguous expressions using mixed
types:

1 + 1.75 # int + float always coerces to float
1 + "2" # int + str always coerces to int
1 & "2" # int & str always coerces to str

but since & is used for integer bitwise-and, and + is used for both
concatenation and addition, we can't, and so Python raises an exception.

For arithmetic, there is an unambiguous hierarchy of types, the numeric
tower, which tells us which types coerce to what, e.g.:

int -> float -> complex

But there is no such hierarchy when it comes to (say) mixed strings and
lists, etc., and hence Python raises an exception rather than guessing
which type you wanted as the result.

This is completely irrelevant when it comes to bools -- we don't have to
coerce a value into another type, we just need to know if it is something
or nothing. The object itself is best able to make that decision, hence
delegating it to a protocol and method:

- If the object is a container, and it has a length of 0, it is empty
and hence nothing (falsey); if it has a non-zero length, it is non-empty
and hence something (truthy).

- Otherwise ask the container whether it is something or nothing by
calling __nonzero__ (the original name) or __bool__.


Python makes a rather big blunder, at least from the perspective of
consistency. Bools are ints:

py> issubclass(bool, int)
True
py> isinstance(True, int)
True
py> isinstance(False, int)
True

but there are things that can be converted into bools that can't be
converted into ints, even though bools *are* ints! Contradiction.

py> x = [None, 42, '']
py> bool(x)
True
py> int(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'list'


Since x can be converted into True, and True == 1, you should be able to
convert x into 1. But that's crazy, since x = [None, 42, ''].

*shrug* I don't call this a gotcha, but it is one of the more ugly
consequences of Python's bool implementation.


> Can you
> reduce this to the absurd? Or will you just choose to ignore this valid
> point?

Mu. (Neither true nor false.)



--
Steven

Steven D'Aprano

unread,
Jul 16, 2012, 4:05:16 AM7/16/12
to
On Sun, 15 Jul 2012 19:41:34 -0700, Ranting Rick wrote:

> Short circuitry is a powerful tool! But why the heck would your
> sequences ever be None? Are you using None as a default? And if so, why
> not use an empty sequence instead ([], {}, "")?


Mostly for explicitness. I want to be able to say that there is
*explicitly* no seq, not merely that it happens to be a list which right
now is empty but later might not be. Using None for missing values is
idiomatic Python.

You should approve, if only for the sake of consistency: None is meant to
be used as "no such value", and that's exactly how I am using it, even at
the cost of my convenience when writing the code.


--
Steven

Serhiy Storchaka

unread,
Jul 16, 2012, 9:01:45 AM7/16/12
to pytho...@python.org
On 15.07.12 19:50, Rick Johnson wrote:
> We must NEVER present "if" in such confusing manner as ExampleA.

## EXAMPLE C ##
py> if bool(money) == True:
... do_something()

Albert van der Horst

unread,
Jul 16, 2012, 1:57:45 PM7/16/12
to
In article <b2971c4d-0769-4d03...@x39g2000yqx.googlegroups.com>,
Ranting Rick <rantingri...@gmail.com> wrote:

>We DON'T want Python to silently convert "cost" to a string. What we
>DO want is to force the author to use the str function thereby making
>the conversion explicit.

We do want Python to silently convert "cost" to a string in the
proper context.

cost= 3.75
print( cost )

>
>Same with converting objects to bools.

I think "if" is sufficient context to convert something to a boolean.
It now is a matter of good taste and what fits best in Python as a
whole. Nothing to be dogmatic about.

Groetjes Albert

--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Albert van der Horst

unread,
Jul 16, 2012, 2:03:56 PM7/16/12
to
In article <50038364$0$29995$c3e8da3$5496...@news.astraweb.com>,
Steven D'Aprano <steve+comp....@pearwood.info> wrote:
>On Sun, 15 Jul 2012 18:21:06 -0700, Ranting Rick wrote:
>
>> If HOWEVER we want to "truth test" an object (as in: "if obj") we should
>> be FORCED to use the bool! Why? Because explicit is better than implicit
>
>And this is why Rick always writes code like:
>
>integer_value_three = int(1) + int(2)
>assert (int(integer_value_three) == \
> int(3) is True) is True, str("arithmetic failed")
>list_containing_three_values_which_are_all_integers_but_might_later_have_more_or_fewer_values_or_other_types = list([1, 2, integer_value_three])
>
>because you can never have too much explicitness. Who wouldn't want
>to read code like that?

Java programmers?

(Couldn't resist ;-) )

>--
>Steven
Message has been deleted

Ethan Furman

unread,
Jul 16, 2012, 2:13:33 PM7/16/12
to pytho...@python.org
Steven D'Aprano wrote:
> On Sun, 15 Jul 2012 10:19:16 -0600, Ian Kelly wrote:
>> On Sun, Jul 15, 2012 at 4:56 AM, Steven D'Aprano wrote:
>>> (For the record, I can only think of one trap for the unwary: time
>>> objects are false at *exactly* midnight.)
>>
>> Ugh, that's irritating. I can't think of any scenario where I would
>> ever want the semantics "if timeval (is not midnight):".
>
> Yes, it is a genuine gotcha. Time values are numbers, and zero is falsey,
> so midnight is falsey even though it shouldn't be.
>
> There's no good solution here, since we have a conflict between treating
> time values as time values ("midnight is nothing special") and as numbers
> ("midnight == 0 which is falsey").

--> import datetime
--> mn = datetime.time(0)
--> mn
datetime.time(0, 0)
--> mn == 0
False

Apparently, midnight does not equal zero. Possibly because it should be
truthy. ;)

~Ethan~

Andrew Berg

unread,
Jul 16, 2012, 4:33:08 PM7/16/12
to comp.lang.python
On 7/15/2012 9:38 PM, Steven D'Aprano wrote:
>> I would expect None to mean "doesn't exist" or "unknown" or
>> something like that - e.g., a value of 0 means 0 jelly beans in the jar
>> and None means there isn't a jar.
>
> How you interpret some_variable = None depends on what some_variable
> represents. If some_variable represents "number of jelly beans in a jar",
> then that should be 0 if there is no jar.
What is None supposed to mean then, and what should I do when I have to
make a distinction between "doesn't exist" and "empty"? Sure, if I need
to count the total number of jelly beans in all my stores, the
distinction is meaningless, but if I need to find out which stores sell
jelly beans so I know which stores need to be restocked, the distinction
is quite important.
--
CPython 3.3.0b1 | Windows NT 6.1.7601.17803

Ethan Furman

unread,
Jul 16, 2012, 4:54:32 PM7/16/12
to comp.lang.python
Andrew Berg wrote:
> On 7/15/2012 9:38 PM, Steven D'Aprano wrote:
>>> I would expect None to mean "doesn't exist" or "unknown" or
>>> something like that - e.g., a value of 0 means 0 jelly beans in the jar
>>> and None means there isn't a jar.
>>
>> How you interpret some_variable = None depends on what some_variable
>> represents. If some_variable represents "number of jelly beans in a jar",
>> then that should be 0 if there is no jar.
>
> What is None supposed to mean then, and what should I do when I have to
> make a distinction between "doesn't exist" and "empty"? Sure, if I need
> to count the total number of jelly beans in all my stores, the
> distinction is meaningless, but if I need to find out which stores sell
> jelly beans so I know which stores need to be restocked, the distinction
> is quite important.

I'm not sure what Steven was trying to say there, but for me:

jar with no jellybeans == 0

no jar == None

~Ethan~

Laszlo Nagy

unread,
Jul 16, 2012, 5:17:58 PM7/16/12
to pytho...@python.org

>> ...
>>
>> Traceback (most recent quip last):
>> Author: "<DeAprano>", line 7, in <post>
>> LogicalFallacyError: "Reductio ad absurdum"
>
> Deary deary me Rick. Reductio ad adsurdum is not a fallacy. It is a
> counter-argument to an argument or claim, by showing that the premise of
> the original claim leads to an absurd conclusion.
>
> You have claimed that we should always be explicit whenever we write. But
> you do not actually live up to your own advice, because you can't: it is
> absurd to try to be explicit about everything all the time. You have
> misunderstood the purpose of the Zen of Python: it is not to claim that
> everything should be explicit, but to avoid code that is hard to
> understand because things which need to be explicit for clarity are
> implied by other parts of your code.

I agree. There is no pont in abolutizing explicitness anyway. It is not
a yes/no question. You cannot tell that somebody is "not explicit". It
is not something that can be decided. But you can say that he was "not
explicit enough" in a concrete case. There is an accepted level of
explicitness. You can probably always be more expicit, or less explicit.
Being more explicit is not the goal. But is a good practice to be more
explicit if it helps you achieve the real goal. For example, writting a
program that can be maintained easily.

Laszlo Nagy

unread,
Jul 16, 2012, 5:26:18 PM7/16/12
to pytho...@python.org

> This syntax is explicit *enough*. We don't need to be any more
> explicit.
>
> But if you are going to argue that "if obj" is *explicit enough*, then
> apply your argument consistently to "String"+1.75 also. Why must we be
> explicit about string conversion BUT not boolean conversion? Can you
> reduce this to the absurd? Or will you just choose to ignore this
> valid point?
Not all decisions in Python are based purely on the "explicit enough"
thing. Some things in the language cannot be explained using
explicitness alone. So, when we cannot fully explain the ' why
String+1.75 ' question with statements about explicitness then it
doesn't mean that anybody lied or wrote something wrong. :-)


Steven D'Aprano

unread,
Jul 16, 2012, 8:43:47 PM7/16/12
to
The existence of a jar or no jar is irrelevant to the question of how
many jellybeans there are. They are two different things, and therefore
need two different values. There are many ways to implement this.

# One way
jar_exists = True # or possibly False
jellybeans = 42 # or whatever the number is, possibly 0

# Another way
jar = Jar(number_of_beans=jellybeans) if jar_exists else None
jellybeans = jar.jellybeans if jar is not None else 0

If you have many jars, and you want to count the total number of
jellybeans:

total = sum(jar.jellybeans for jar in jars if jar is not None)


Strictly speaking, the "is not None" is redundant, but it expresses the
intent better than the alternative. Assuming that jar instances follow
the standard Python API for containers, and is treated as falsey when it
has a jellybean count of zero:

total = sum(jar.jellybeans for jar in jars if jar)
# relies on the fact that adding zero to a number makes
# no difference, so you can safely leave zeroes out


Here's a case where you *must* distinguish between empty jars and None:

number_of_jars = sum(1 for jar in jars if jar is not None)

and a case where you *shouldn't*:

number_of_nonempty_jars = sum(1 for jar in jars if jar)

Of course you can write this:

number_of_nonempty_jars = sum(
1 for jar in jars if jar is not None and jar.jellybeans > 1
)

but that overwhelms the code with incidental implementation details about
jellybean counts, which is prone to bugs. (Did you spot it?)

Even jar.isempty() would be better, but better still is to *not* invent
your own container API but to use the standard Python one instead and
define an appropriate __nonzero__ (Python 2) or __bool__ (Python 3)
method.

If I insist on making a single object do duty for both the jar and the
jellybean count, then I need a "null jar object", and I probably end up
with something like this:

Jar(number_of_beans=None) => null jar object with jar.jellybeans = 0
Jar(number_of_beans=0) => normal jar object with jar.jellybeans = 0
Jar(number_of_beans=42) => normal jar object with jar.jellybeans = 42

and then my code becomes even more complicated and less understandable,
but hey, it's *my* code and I can do whatever damn-fool thing I like!


--
Steven

Steven D'Aprano

unread,
Jul 16, 2012, 9:12:47 PM7/16/12
to
On Mon, 16 Jul 2012 13:28:14 -0400, Dennis Lee Bieber wrote:

> On 16 Jul 2012 02:38:35 GMT, Steven D'Aprano
> <steve+comp....@pearwood.info> declaimed the following in
> gmane.comp.python.general:
>
>> On Sun, 15 Jul 2012 12:02:37 -0500, Andrew Berg wrote:
>>
>>
>> > Okay, I see the value in this, but I don't understand why None has a
>> > truth value.
>>
>> And this is exactly the sort of mental confusion that Laura Crichton
>> warned about (see the link I included earlier).
>>
>>
> Would one rather have the behavior seen in SQL for Null?
> http://www.firebirdsql.org/file/documentation/reference_manuals/
user_manuals/Firebird-Null-Guide.pdf


That's a 51 page document. I'm not sure I know which behaviour you are
referring to.

Seems to me that the Firebird NULL object is closer to a float NaN than
to Python's None, except that Firebird treats comparisons with NULL as
returning a NULL, while Python treats comparisons with NaN as True or
False.

Both behaviours are reasonable, but the Firebird behaviour seems to be
more error-prone.


> Hey, let's turn the IF statement into tri-state logic...
[...]

I'm not sure if you're being sarcastic here or not. Ternary logic is
perfectly reasonable, although I expect that it would be error-prone
because programmers would forget the "unknown" clause all the time. It
looks like Firebird implements the variety of ternary logical called
"Keene logic".

Of course, ternary logic can always be re-written in binary terms.
Assuming that UNKNOWN evaluates as false:

if flag:
true-clause
else:
if flag is UNKNOWN:
unknown-clause
else:
false-clause



--
Steven

Steven D'Aprano

unread,
Jul 16, 2012, 9:13:39 PM7/16/12
to
On Tue, 17 Jul 2012 01:12:47 +0000, Steven D'Aprano wrote:

> It
> looks like Firebird implements the variety of ternary logical called
> "Keene logic".

Oops, I meant "Kleene".



--
Steven

Andrew Berg

unread,
Jul 16, 2012, 9:22:18 PM7/16/12
to comp.lang.python
On 7/15/2012 3:28 PM, Terry Reedy wrote:
> Because everything does (or should).
I can see how truth testing for empty values is convenient, but perhaps
objects should only have a truth value if explicitly given one -
particularly in cases where such a value wouldn't be obvious or the
obvious value isn't the actual one:

>>> c = types.SimpleNamespace()
>>> if c: print('c')
...
c
>>> c.__dict__
{}

Would it not be reasonable to expect an empty namespace to have a truth
value of False since [] and friends do? It's a bit of a gray area for an
object defined by "class C: pass", but this is *specifically intended*
to be a namespace. Why should things like functions or exceptions have
truth values?

Note: For those who aren't aware, types.SimpleNamespace was added in
3.3.0b1.

On a side note, I tend to do this:

x = some_list
try:
y = x.pop()
except IndexError:
do_something_else()

rather than:

if x:
y = x.pop()
else:
do_something_else()

but of course that only applies to empty lists/sets/related and not
0/0.0/None and only when I want something from the list/set/whatever.

rusi

unread,
Jul 16, 2012, 9:45:51 PM7/16/12
to
On Jul 15, 9:50 pm, Rick Johnson <rantingrickjohn...@gmail.com> wrote:
> On Sunday, July 15, 2012 11:19:16 AM UTC-5, Ian wrote:
> > On Sun, Jul 15, 2012 at 4:56 AM, Steven D'Aprano
> > <steve+comp.lang.pyt...@pearwood.info> wrote:
> > > (For the record, I can only think of one trap for the unwary: time
> > > objects are false at *exactly* midnight.)
>
> > Ugh, that's irritating.  I can't think of any scenario where I would
> > ever want the semantics "if timeval (is not midnight):".  This
> > reinforces the point that if you only want to test whether you have
> > None, you should use "is not None" rather than relying on __bool__.
>
> I think this issue is not so much a "bool test" vs "type test", but more an ambiguous syntax
> issue.

If you know some English, its clear that if and while create bool
contexts.
[If you know English but have not studied logic the 'if/while' make
sense whereas 'bool' is gobbledygook]

The issue is not where the cast goes to -- this clearly is bool
But where the cast comes from -- which requires tracing program-paths

Andrew Berg

unread,
Jul 16, 2012, 9:57:43 PM7/16/12
to comp.lang.python
On 7/16/2012 7:43 PM, Steven D'Aprano wrote:
> The existence of a jar or no jar is irrelevant to the question of how
> many jellybeans there are. They are two different things, and therefore
> need two different values. There are many ways to implement this.
I have a better real example, but I opted not to use it before since it
requires some explanation - IRC messages.
A client-to-server message has the basic form of b'COMMAND arguments
:message' (e.g. b'PRIVMSG #channel :hi guys!'). Some commands have no
message part because there is no message associated with it (e.g. b'JOIN
#channel') and there is at least one where there is a big difference
between a blank message (b'') and no message - b'TOPIC #channel' is a
request for the topic while b'TOPIC #channel :' clears the topic since
the part after the b':' is b'' (b'TOPIC #channel :Welcome to #channel'
sets the topic to "Welcome to #channel"). In my code, I would have an
object representing a message rather than parsing it multiple times. If
the message
attribute is not None, I send b'{command} {args} :{message}', otherwise
b'{command} {args}'. If I considered '' falsey, either I would require
all messages to have ":" (which would not actually be part of the
message) or have any request to view the topic as a channel op clear the
topic. This would apply to the server parsing the message as well. A few
other commands have messages optional as well, but they are not as
serious as TOPIC.

I could do:

if has_message:
send('{command} {args} :{message}')
else:
send('{command} {args}')

but then I'd have to make sure has_message stays accurate since message
won't necessarily be. Or maybe I could leave message undefined and catch
the appropriate exception. However, using None is the cleanest and most
obvious.

I know Rick likes to troll, but I do agree with him that "if something:"
for arbitrary objects can be ambiguous or confusing. I don't think
if/while must have True or False, but not every object has an obvious
truth value.

Chris Angelico

unread,
Jul 16, 2012, 10:13:48 PM7/16/12
to pytho...@python.org
On Tue, Jul 17, 2012 at 11:57 AM, Andrew Berg <bahamut...@gmail.com> wrote:
> I could do:
>
> if has_message:
> send('{command} {args} :{message}')
> else:
> send('{command} {args}')
>
> but then I'd have to make sure has_message stays accurate since message
> won't necessarily be. Or maybe I could leave message undefined and catch
> the appropriate exception. However, using None is the cleanest and most
> obvious.
>
> I know Rick likes to troll, but I do agree with him that "if something:"
> for arbitrary objects can be ambiguous or confusing. I don't think
> if/while must have True or False, but not every object has an obvious
> truth value.

Using None when '' is a valid token is the right thing to do (see
also, for instance, SQL's NULL, which is often used the same way). And
then you have gone away from the language's idea of whether a string
is truthy or not, so you get explicit:

if message is not None:
send('{command} {args} :{message}')

Easy! Or if the language went the other way ("all strings are true"),
it would be the other way around, you'd use "if message!=''" for the
other case. It's not a difficult problem.

Carry on bikeshedding. :)

ChrisA

Steven D'Aprano

unread,
Jul 17, 2012, 12:11:04 AM7/17/12
to
On Mon, 16 Jul 2012 20:22:18 -0500, Andrew Berg wrote:

> On 7/15/2012 3:28 PM, Terry Reedy wrote:
>> Because everything does (or should).
> I can see how truth testing for empty values is convenient, but perhaps
> objects should only have a truth value if explicitly given one -
> particularly in cases where such a value wouldn't be obvious or the
> obvious value isn't the actual one:

You have run into Python's default behaviour: objects are treated as
something by default. If you want them to represent nothing, you have to
explicitly code that case.

py> o = object()
py> bool(o)
True

Yet again, thinking about something versus nothing instead of true/false
makes the behaviour both obvious and correct: of course an object should
be treated as something (true-like) rather than nothing (false-like) by
default, it's an *object* is it not?

If you want your type to implement non-default semantics, such as
container semantics, then you need to code it.


>>>> c = types.SimpleNamespace()
>>>> if c: print('c')
> ...
> c
>>>> c.__dict__
> {}
>
> Would it not be reasonable to expect an empty namespace to have a truth
> value of False since [] and friends do? It's a bit of a gray area for an
> object defined by "class C: pass", but this is *specifically intended*
> to be a namespace. Why should things like functions or exceptions have
> truth values?

And this is a good life-lesson that any class called "SimpleFoo" will not
stay simple for long.

If you are right that SimpleNamespace should be treated as a container,
then it should implement container semantics. Since it doesn't, that is
either:

1) a bug; or
2) a triumph of laziness over correctness

I imagine though that the Python dev's answer will basically be #2: "it
isn't a container, it just behaves a little bit like a container, except
when it doesn't" kinda thing. But feel free to report it as a bug and see
what happens.

(This is not *entirely* wrong, because SimpleNamespace certainly doesn't
*claim* to be a container, nor does it expose the full container API. But
it should, even if that means it is no longer quite so simple.)



--
Steven

Ranting Rick

unread,
Jul 17, 2012, 12:18:18 AM7/17/12
to
On Jul 16, 11:11 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:

> I imagine though that the Python dev's answer will basically be #2: "it
> isn't a container, it just behaves a little bit like a container, except
> when it doesn't" kinda thing.

The emperor has no clothes!

Devin Jeanpierre

unread,
Jul 17, 2012, 12:18:28 AM7/17/12
to Steven D'Aprano, pytho...@python.org
On Mon, Jul 16, 2012 at 12:03 AM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:
>
>> For example, instead of "if stack:" or "if bool(stack):", we could use
>> "if stack.isempty():". This line tells us explicitly that stack is a
>> container.
>
> isempty is not a container method.

Your entire reply is predicated on this idea that I was talking about
writing classes with this extra "isempty" method.

No. I was talking about having "isempty" be part of the collection
interface, and eliminating polymorphic bool conversion.

If you were confused, instead of assuming I meant something patently
absurd, you should have asked for a clarification.

-- Devin

Chris Angelico

unread,
Jul 17, 2012, 12:24:09 AM7/17/12
to pytho...@python.org
The Troll's New Point.

ChrisA

Steven D'Aprano

unread,
Jul 17, 2012, 12:39:33 AM7/17/12
to
On Mon, 16 Jul 2012 20:57:43 -0500, Andrew Berg wrote:

> I have a better real example, but I opted not to use it before since it
> requires some explanation - IRC messages. A client-to-server message has
> the basic form of b'COMMAND arguments :message' (e.g. b'PRIVMSG #channel
> :hi guys!'). Some commands have no message part because there is no
> message associated with it (e.g. b'JOIN #channel') and there is at least
> one where there is a big difference between a blank message (b'') and no
> message - b'TOPIC #channel' is a request for the topic while b'TOPIC
> #channel :' clears the topic since the part after the b':' is b''
> (b'TOPIC #channel :Welcome to #channel' sets the topic to "Welcome to
> #channel").

Okay, so you have two distinct "nothing" states when considering the
message part of an IRC command: the empty string, and missing.

That's okay. Floats have two zeroes (+0.0 and -0.0); complex numbers have
four. (Although they try hard to hide that distinction from you.)

There's nothing that says that you can only have a single falsey value in
a type, or that you might not sometimes wish to distinguish between
different false-like states. You need to distinguish between the many
different true-like messages, so you should not be surprised that you
need to distinguish between two false-like messages.

There are many ways to implement this. Here are just the most obvious:

1) a Command object where the message attribute is optional, but if
present, it is always a string;

2) a Command object where the message attribute is always present, but
can be a string or some non-string sentinel value (e.g. None);

3) a string, where the message attribute is determined by the location
of the colon, if any

4) a tuple with either two or three fields: (command, channel [,message])



> In my code, I would have an object representing a message
> rather than parsing it multiple times. If the message
> attribute is not None, I send b'{command} {args} :{message}', otherwise
> b'{command} {args}'.

Clear and obvious. Nothing wrong with that.


> I could do:
>
> if has_message:
> send('{command} {args} :{message}')
> else:
> send('{command} {args}')
>
> but then I'd have to make sure has_message stays accurate since message
> won't necessarily be.

Yes, keeping a separate variable is a mug's game. Encapsulate it in the
Command object, and have the Command object responsible for keeping it in
sync (assuming it is mutable), or just make Command immutable and be done
with it.


> Or maybe I could leave message undefined and catch
> the appropriate exception. However, using None is the cleanest and most
> obvious.

Yes it is. What's your point?

You've discovered a real-world situation where you can't collapse the
entire universe of valid values into just two, True and False, without
losing information. Did you think that this would be surprising?

Python developers often talk about interpreting objects "in a boolean
context" -- that's a pretty big hint that the semantics are to collapse
the value into two states. If you need three (or four, or fifty)
distinguishable states, then obviously boolean context will not solve
your problem. I never said it would.



--
Steven
Message has been deleted

Andrew Berg

unread,
Jul 17, 2012, 12:44:02 AM7/17/12
to comp.lang.python
On 7/16/2012 11:11 PM, Steven D'Aprano wrote:
> If you are right that SimpleNamespace should be treated as a container,
> then it should implement container semantics. Since it doesn't, that is
> either:
>
> 1) a bug; or
> 2) a triumph of laziness over correctness
>
> I imagine though that the Python dev's answer will basically be #2: "it
> isn't a container, it just behaves a little bit like a container, except
> when it doesn't" kinda thing. But feel free to report it as a bug and see
> what happens.
I'm not saying it's necessarily wrong, but I do think it quacks a lot
like a container, even though it isn't one, especially if you're
listening for quacks instead of looking for ducks.

Steven D'Aprano

unread,
Jul 17, 2012, 12:47:45 AM7/17/12
to
On Sun, 15 Jul 2012 10:56:57 +0000, Steven D'Aprano wrote:

> On Sun, 15 Jul 2012 03:34:46 -0500, Andrew Berg wrote:
>
>> This has probably been discussed before,
>
> By the hoary hosts of Hoggoth, has it ever!

Dammit this topic is a tar-baby.

:(



--
Steven

Andrew Berg

unread,
Jul 17, 2012, 1:19:48 AM7/17/12
to comp.lang.python
On 7/16/2012 11:39 PM, Steven D'Aprano wrote:
> If you need three (or four, or fifty)
> distinguishable states, then obviously boolean context will not solve
> your problem. I never said it would.
That is the impression I got from this statement:

> How you interpret some_variable = None depends on what some_variable
> represents. If some_variable represents "number of jelly beans in a jar",
> then that should be 0 if there is no jar.
But I guess you misunderstood (or were just picking at) the example.

Of course I can (and do) explicitly use "if x is not None" when testing
for None, but I don't want a bug being obscured because "if x" accepts
an erroneous value that it interprets as truthy or falsey. I could be
explicit when testing for things other than None, but apparently that's
un-Pythonic.

To put it in duck-typing terms, why should everything have to quack like
True or False? Sure, I can see why 1 quacks like True or [] quacks like
False, but I don't see why say, a Logger or function should quack like
either. Should a Thread object be True if it's been started and False
otherwise?

If it truly is about something vs. nothing, why is a NameError (or
AttributeError) raised when testing with an undefined variable? Being
undefined quacks like nothing, doesn't it?

Steven D'Aprano

unread,
Jul 17, 2012, 2:14:09 AM7/17/12
to
My mistake.



--
Steven

Steven D'Aprano

unread,
Jul 17, 2012, 2:25:43 AM7/17/12
to
On Tue, 17 Jul 2012 00:18:28 -0400, Devin Jeanpierre wrote:

> On Mon, Jul 16, 2012 at 12:03 AM, Steven D'Aprano
> <steve+comp....@pearwood.info> wrote:
>> On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:
>>
>>> For example, instead of "if stack:" or "if bool(stack):", we could use
>>> "if stack.isempty():". This line tells us explicitly that stack is a
>>> container.
>>
>> isempty is not a container method.
>
> Your entire reply is predicated on this idea that I was talking about
> writing classes with this extra "isempty" method.
>
> No. I was talking about having "isempty" be part of the collection
> interface, and eliminating polymorphic bool conversion.

It already is part of the collection interface: it is spelled __nonzero__
(Python 2) or __bool__ (Python 3), and like all dunder methods, it is
called automatically for you when you use the right syntax:

# do this
x = n - len(y)
if x and y:
...

# don't do this
x = n.__sub__(y.__len__())
if x.__nonzero__() and y.__nonzero__():
...


--
Steven

Steven D'Aprano

unread,
Jul 17, 2012, 3:08:33 AM7/17/12
to
On Tue, 17 Jul 2012 00:19:48 -0500, Andrew Berg wrote:

> To put it in duck-typing terms, why should everything have to quack like
> True or False? Sure, I can see why 1 quacks like True or [] quacks like
> False, but I don't see why say, a Logger or function should quack like
> either.

The default behaviour is that every object is something, hence true-like,
unless explicitly coded to be treated as false-like. Since both loggers
and functions are objects, they are true-like unless the default is
overridden.

If you don't like that simple, consistent model for truthiness, feel free
to design your own language with a different model. Or you can use any
one of the dozens of other existing languages which do what you want.


> Should a Thread object be True if it's been started and False
> otherwise?

If you, the Thread class author, want it to be, you can make it so.


> If it truly is about something vs. nothing, why is a NameError (or
> AttributeError) raised when testing with an undefined variable? Being
> undefined quacks like nothing, doesn't it?

Not really. It doesn't quack like anything.


Are you suggesting that if x doesn't exist, you want this behaviour?


>>> del x # make sure x doesn't exist
>>> if x: print('cheese')
... else:
... print('x is falsy')
... print(x)
...
x is falsy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined


OH MAN, that would be SO AWESOME, we should like so do it!!!

(I'm being sarcastic, in case it's not obvious.)



--
Steven

John Nagle

unread,
Jul 17, 2012, 3:26:50 AM7/17/12
to
On 7/15/2012 1:34 AM, Andrew Berg wrote:
> This has probably been discussed before, but why is there an implicit
> conversion to a boolean in if and while statements?
>
> if not None:
> print('hi')
> prints 'hi' since bool(None) is False.
>
> If this was discussed in a PEP, I would like a link to it. There are so
> many PEPs, and I wouldn't know which ones to look through.
>
> Converting 0 and 1 to False and True seems reasonable, but I don't see
> the point in converting other arbitrary values.

Because Boolean types were an afterthought in Python. See PEP 285.
If a language starts out with a Boolean type, it tends towards
Pascal/Ada/Java semantics in this area. If a language backs
into needing a Boolean type, as Python and C did, it tends to have
the somewhat weird semantics of a language which can't quite decide
what's a Boolean. C and C++ have the same problem, for exactly the
same reason - boolean types were an afterthought there, too.

John Nagle

Andrew Berg

unread,
Jul 17, 2012, 4:23:27 AM7/17/12
to comp.lang.python
On 7/17/2012 2:08 AM, Steven D'Aprano wrote:
> The default behaviour is that every object is something, hence true-like,
> unless explicitly coded to be treated as false-like. Since both loggers
> and functions are objects, they are true-like unless the default is
> overridden.
I am aware of the default behavior, but the reason for it still eludes me.

> Are you suggesting that if x doesn't exist, you want this behaviour?
I don't want that, but I am suggesting that it would be consistent with
the idea of "something or nothing".

Chris Angelico

unread,
Jul 17, 2012, 4:32:53 AM7/17/12
to pytho...@python.org
On Tue, Jul 17, 2012 at 6:23 PM, Andrew Berg <bahamut...@gmail.com> wrote:
> On 7/17/2012 2:08 AM, Steven D'Aprano wrote:
>> The default behaviour is that every object is something, hence true-like,
>> unless explicitly coded to be treated as false-like. Since both loggers
>> and functions are objects, they are true-like unless the default is
>> overridden.
> I am aware of the default behavior, but the reason for it still eludes me.

There has to be something. This way, you can use None in place of any
object, in the same way that a null pointer would be used in C; any
object is true, None isn't. What other default makes more sense?

ChrisA

Devin Jeanpierre

unread,
Jul 17, 2012, 5:38:07 AM7/17/12
to Steven D'Aprano, pytho...@python.org
On Tue, Jul 17, 2012 at 2:25 AM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> It already is part of the collection interface: it is spelled __nonzero__
> (Python 2) or __bool__ (Python 3), and like all dunder methods, it is
> called automatically for you when you use the right syntax:

You're still ignoring what I actually said.

-- Devin

Ethan Furman

unread,
Jul 17, 2012, 8:35:52 AM7/17/12
to comp.lang.python
Andrew Berg wrote:
> To put it in duck-typing terms, why should everything have to quack like
> True or False? Sure, I can see why 1 quacks like True or [] quacks like
> False, but I don't see why say, a Logger or function should quack like
> either. Should a Thread object be True if it's been started and False
> otherwise?

True and False are red herrings. It is more appropriate to think that
True quacks like something and False like nothing than the other way 'round.

Maybe some examples from my own code will help:

DbfTable --> True if any records in table, False otherwise

DbfIndex --> True if any records in index, False otherwise

DbfList --> True if any records in list, False otherwise

DbfDate --> True if a date, False otherwise (could be eight spaces
instead of a real date)

DbfDateTime --> True if a datetime, False otherwise

DbfRecord --> True always

DbfRecordTemplate --> True always

DbfRecordVaporware --> False always

While I could have DbfRecord be False if, for example, it had no data
stored in it, I have no use case for that scenario so haven't bothered.
Also, at this point I am using the distinction of True/False with
regards to records to determine if I have a real record (True means a
record/template I can read/write, False means I don't).


> If it truly is about something vs. nothing, why is a NameError (or
> AttributeError) raised when testing with an undefined variable? Being
> undefined quacks like nothing, doesn't it?

It's about /representing/ something vs. nothing. An undefined name isn't
representing anything (except a bug, of course ;).

~Ethan~

Laszlo Nagy

unread,
Jul 17, 2012, 8:43:11 AM7/17/12
to pytho...@python.org
On 2012-07-17 10:23, Andrew Berg wrote:
> I don't want that, but I am suggesting that it would be consistent with
> the idea of "something or nothing".
Don't confuse names and objects. You can only test the truth value of
objects. If you don't have a name in a namespace, then it means you
don't have a tool to have a reference to anything (including the False
object).

Using the same logic you could also say that not giving any condition to
the "if" statement should be evaluated as False:

if:
print "This never gets executed"

But it makes no sense.

Laszlo Nagy

unread,
Jul 17, 2012, 8:59:44 AM7/17/12
to Steven D'Aprano, pytho...@python.org

> Not really. It doesn't quack like anything.
Actually, there is no "it". So we cannot talk about how it quacks. :-D

Terry Reedy

unread,
Jul 17, 2012, 1:57:19 PM7/17/12
to pytho...@python.org
On 7/17/2012 4:23 AM, Andrew Berg wrote:
> On 7/17/2012 2:08 AM, Steven D'Aprano wrote:
>> The default behaviour is that every object is something, hence true-like,
>> unless explicitly coded to be treated as false-like. Since both loggers
>> and functions are objects, they are true-like unless the default is
>> overridden.
> I am aware of the default behavior, but the reason for it still eludes me.

Ultimately, because Guido's intuition said that the current behavior is
the best. I happen to agree on this one.

Quoting from elsewhere:
> If it truly is about something vs. nothing, why is a NameError (or
> AttributeError) raised when testing with an undefined variable?


Python does not have 'undefined variable' objects in the way that some
other languages do. There is nothing to test 'with'. 'if
undefined_name:' raises NameError because all uses of undefined_names
other that assignments do. The actually execution order is expression
first, then if. You can explicitly test "'some_name' in namespace" or
"hasattr(obj, 'somename') by turning the possibly undefined name into a
string object.

--
Terry Jan Reedy



alex23

unread,
Jul 17, 2012, 9:35:09 PM7/17/12
to
On Jul 17, 6:23 pm, Andrew Berg <bahamutzero8...@gmail.com> wrote:
> On 7/17/2012 2:08 AM, Steven D'Aprano wrote:
> > The default behaviour is that every object is something, hence true-like,
> > unless explicitly coded to be treated as false-like. Since both loggers
> > and functions are objects, they are true-like unless the default is
> > overridden.
>
> I am aware of the default behavior, but the reason for it still eludes me.

Because it makes it simple to distinguish between having an object and
not having one without having to explicitly test for it each time.

db = connect("my:db") # or None if the connection failed
if db:
<do something>

I find that usage to be incredibly intuitive.

Rick Johnson

unread,
Feb 8, 2013, 12:16:39 AM2/8/13
to
On Monday, July 16, 2012 7:43:47 PM UTC-5, Steven D'Aprano wrote:
>
> [...]
>
> If I insist on making a single object do duty for both the jar and the
> jellybean count, then I need a "null jar object", and I probably end up
> with something like this:
>
> Jar(number_of_beans=None) => null jar object with jar.jellybeans = 0
> Jar(number_of_beans=0) => normal jar object with jar.jellybeans = 0
> Jar(number_of_beans=42) => normal jar object with jar.jellybeans = 42
>
> and then my code becomes even more complicated and less understandable,
> but hey, it's *my* code and I can do whatever damn-fool thing I like!

Indeed, but why would you create a jellybean jar and never put any jelly beans into the jar? If you are doing so because you want to loop over a number of objects and never get a NameError than you need to study up on a few language features you may be unaware of:

1. block: try/except
2. statement: if
3. function: hasattr(obj, name)

Creating /any/ object that is never utilized is code smell; I don't care how extravagant your explanations are either -- and boy did you go to some great efforts to explain this line of argument. I'm impressed! ;-)

Rick Johnson

unread,
Feb 8, 2013, 12:53:08 AM2/8/13
to
On Monday, July 16, 2012 8:45:51 PM UTC-5, rusi wrote:
> On Jul 15, 9:50 pm, Rick Johnson <rantingrickjohn...@gmail.com> wrote:

> > I think this issue is not so much a "bool test" vs "type
> > test", but more an ambiguous syntax issue.
> >
>
> If you know some English, its clear that if and while
> create bool contexts.

Wrong. "if and "while" do not /create/ anything. On a syntactical level they merely /suggest/ to the reader that the following statement is expected to be a boolean value. It is the /statement/ itself that creates the boolean value, not the keywords!

Observe:

0 == 0 -> True
isinstance("5", int) -> False

You see, "if" and "while" don't create anything, in reality they merely execute a block of code depending on the value of the statement that follows the keyword. "if" and "while" are only *logical switches* and nothing more. You could write a simple quasi-example of "if" as a function like this:

def if_(value):
if not value:
return
# do_something_here


Those previous statements where /explicit/, and as such need no bool() function to resolve their Boolean values, however, consider the following /implicit/ conversions to Boolean:

[] -> False
[1] -> True
"" -> False
"1" -> True
0 -> False
1 -> True
2 -> True
etc...

It is my strong opinion that these types of implicit conversions are evil obfuscations of the truth. If we want to convert an object to a Boolean, then use the bool() function on that object:

bool([]) -> False
bool([1]) -> True
etc...

Heck! Why even have a damn bool function if you're never going to use it?

> [If you know English but have not
> studied logic the 'if/while' make sense whereas 'bool' is
> gobbledygook]

And which Univeristy would you recommend for studying the intricacies of "gobbledygook"? ;-)

Chris Angelico

unread,
Feb 8, 2013, 1:15:23 AM2/8/13
to pytho...@python.org
On Fri, Feb 8, 2013 at 4:53 PM, Rick Johnson
<rantingri...@gmail.com> wrote:
> And which Univeristy would you recommend for studying the intricacies of "gobbledygook"? ;-)

Dunno, where'd you get your degree in logic?

*dives for cover*

ChrisA

Rick Johnson

unread,
Feb 8, 2013, 1:16:12 AM2/8/13
to pytho...@python.org
On Monday, July 16, 2012 11:18:28 PM UTC-5, Devin Jeanpierre wrote:
> On Mon, Jul 16, 2012 at 12:03 AM, Steven D'Aprano wrote:
> > On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:
> >
> >> For example, instead of "if stack:" or "if bool(stack):", we could use
> >> "if stack.isempty():". This line tells us explicitly that stack is a
> >> container.
> >
> > isempty is not a container method.
>
> Your entire reply is predicated on this idea that I was talking about
> writing classes with this extra "isempty" method.

Steven's adherence to this implicit conversion is warping his comprehension of your words. He is so accustomed to "guessing" that it has become second nature for him.

> No. I was talking about having "isempty" be part of the collection
> interface, and eliminating polymorphic bool conversion.

Which i believe is a great idea!

GvR has always been reluctant to incorporate full OOP machinery for some reason. I am not suggesting that Python be 100% OOP, HELL NO! But collections should have had an "isempty" method from the beginning. But the same argument could be made against len, any, all, etc...

But now we are opening a whole bag of cats. What about hasattr, getattr, setattr, type, dir, id, isinstance, issubclass, and many more that could be inherited directly from object; and they should be!

On the flip side i do believe int, float, str, tuple, list, dict... should remain as built-ins, and the obvious: help, input, globals, locals, vars, print, etc...

Python has too many built-ins. I think we need a PyWart on this subject.
Message has been deleted

Rick Johnson

unread,
Feb 8, 2013, 1:16:12 AM2/8/13
to comp.lan...@googlegroups.com, pytho...@python.org
On Monday, July 16, 2012 11:18:28 PM UTC-5, Devin Jeanpierre wrote:
> On Mon, Jul 16, 2012 at 12:03 AM, Steven D'Aprano wrote:
> > On Sun, 15 Jul 2012 22:15:13 -0400, Devin Jeanpierre wrote:
> >
> >> For example, instead of "if stack:" or "if bool(stack):", we could use
> >> "if stack.isempty():". This line tells us explicitly that stack is a
> >> container.
> >
> > isempty is not a container method.
>
> Your entire reply is predicated on this idea that I was talking about
> writing classes with this extra "isempty" method.

Steven's adherence to this implicit conversion is warping his comprehension of your words. He is so accustomed to "guessing" that it has become second nature for him.

> No. I was talking about having "isempty" be part of the collection
> interface, and eliminating polymorphic bool conversion.

Michael Torrie

unread,
Feb 8, 2013, 1:27:09 AM2/8/13
to pytho...@python.org
On 02/07/2013 11:16 PM, Rick Johnson wrote:
> He is so accustomed to "guessing" that it has become second nature
> for him.

I think most of us are guessing as to what you're talking about since
you're responding to a 7 month old thread that I think most people have
long since deleted from their e-mail or nntp readers.

Gotta hand it to you, though. It's working.

Rick Johnson

unread,
Feb 8, 2013, 1:29:37 AM2/8/13
to
On Tuesday, July 17, 2012 8:35:09 PM UTC-5, alex23 wrote:
> On Jul 17, 6:23 pm, Andrew Berg <bahamutzero8...@gmail.com> wrote:
> > On 7/17/2012 2:08 AM, Steven D'Aprano wrote:
> > > The default behaviour is that every object is something, hence true-like,
> > > unless explicitly coded to be treated as false-like. Since both loggers
> > > and functions are objects, they are true-like unless the default is
> > > overridden.
> >
> > I am aware of the default behavior, but the reason for it still eludes me.
>
> Because it makes it simple to distinguish between having an object and
> not having one without having to explicitly test for it each time.

That's a strange thing to say when you go on to provide an example that tests the validity of the object "each and every time":

>
> db = connect("my:db") # or None if the connection failed
> if db:
> <do something>
> I find that usage to be incredibly intuitive.

And i find it to be incredibly asinine.

Consider this:

if connect("my:db") as db:
<do something>

No need to make a call and then test for the validity of the call when you can do both simultaneously AND intuitively.

*school-bell-rings*

Rick Johnson

unread,
Feb 8, 2013, 1:32:56 AM2/8/13
to pytho...@python.org
On Friday, February 8, 2013 12:27:09 AM UTC-6, Michael Torrie wrote:
> On 02/07/2013 11:16 PM, Rick Johnson wrote:
> > He is so accustomed to "guessing" that it has become second nature
> > for him.
>
> I think most of us are guessing as to what you're talking about since
> you're responding to a 7 month old thread that I think most people have
> long since deleted from their e-mail or nntp readers.

Well i know this thread is quite old however i never got a chance to provide a response due to heavy work loads. I feel this subject is very important for the python language, hence my late replies.

Rick Johnson

unread,
Feb 8, 2013, 1:32:56 AM2/8/13
to comp.lan...@googlegroups.com, pytho...@python.org
On Friday, February 8, 2013 12:27:09 AM UTC-6, Michael Torrie wrote:
> On 02/07/2013 11:16 PM, Rick Johnson wrote:
> > He is so accustomed to "guessing" that it has become second nature
> > for him.
>
> I think most of us are guessing as to what you're talking about since
> you're responding to a 7 month old thread that I think most people have
> long since deleted from their e-mail or nntp readers.

Steven D'Aprano

unread,
Feb 8, 2013, 2:22:27 AM2/8/13
to
Rick Johnson wrote:

> Why even have a damn bool function if you're never going to use it?

bool is for converting arbitrary objects into a canonical True or False
flag. E.g. one use-case is if you wish to record in permanent storage a
flag, and don't want arbitrary (possibly expensive) objects to be recorded.

Most of the time, you shouldn't care whether you have a canonical True/False
bool, you should only care whether you have something which duck-types as a
boolean flag: a truthy or falsey value. In Python, all objects duck-type as
flags. The usual interpretation is whether the object represents something
or nothing:

"nothing", or falsey values: None, False, 0, 0.0, '', [], {}, set(), etc.
(essentially, the empty value for whichever type you are considering)

"something", or truthy values: True, 1, 2.5, 'hello world', etc.
(essentially, non-empty values).

Prior to Python 3, the special method __bool__ was spelled __nonempty__,
which demonstrates Python's philosophy towards duck-typing bools.


--
Steven

Mark Lawrence

unread,
Feb 8, 2013, 4:42:39 AM2/8/13
to pytho...@python.org
On 08/02/2013 06:15, Chris Angelico wrote:
> On Fri, Feb 8, 2013 at 4:53 PM, Rick Johnson
> <rantingri...@gmail.com> wrote:
>> And which Univeristy would you recommend for studying the intricacies of "gobbledygook"? ;-)
>
> Dunno, where'd you get your degree in logic?

From the University of Wallamaloo whilst in charge of the sheep dip?

>
> *dives for cover*
>
> ChrisA
>
--
Cheers.

Mark Lawrence

Thomas Rachel

unread,
Feb 8, 2013, 6:11:20 AM2/8/13
to
Am 08.02.2013 07:29 schrieb Rick Johnson:

> Consider this:
>
> if connect("my:db") as db:
> <do something>
>
> No need to make a call and then test for the validity of the call when you can do both simultaneously AND intuitively.

Would be great, but can be emulated with

def ifiter(x):
if x: yield x

for db in ifiter(connect("my:db")):
<do sth with db>

Is not very intuitive, however, but does its job.


Thomas

Steven D'Aprano

unread,
Feb 8, 2013, 6:26:28 AM2/8/13
to
Rick Johnson wrote:

> On Monday, July 16, 2012 7:43:47 PM UTC-5, Steven D'Aprano wrote:

Really Rick? Digging out a post from nearly seven months ago? You must
really be bored silly.



--
Steven

Steven D'Aprano

unread,
Feb 8, 2013, 10:16:42 AM2/8/13
to
Rick Johnson wrote:

> GvR has always been reluctant to incorporate full OOP machinery for some
> reason.

Python is a fully object oriented language. It is *more* object oriented
than, say, Java.

- everything in Python is an object, there is no distinction between "boxed"
and "unboxed" variables;

- modules are objects;

- functions and methods are objects;

- classes are objects in Python, and have their own class (the metaclass);

- metaclasses themselves are also objects, and have classes of their own;

- it's objects all the way down, at least until you reach "type" itself,
which is bootstrapped into existence by the compiler.


Although Python is fully object-oriented, it does not insist on one
particular style of object syntax. It allows procedural and functional
style syntax as well.


> I am not suggesting that Python be 100% OOP, HELL NO! But
> collections should have had an "isempty" method from the beginning. But
> the same argument could be made against len, any, all, etc...

No they shouldn't.

http://effbot.org/pyfaq/why-does-python-use-methods-for-some-functionality-e-g-list-index-but-functions-for-other-e-g-len-list.htm

http://lucumr.pocoo.org/2011/7/9/python-and-pola/

http://mail.python.org/pipermail/python-dev/2008-January/076612.html


Python functions operate as *protocols*. any() and all(), for example, are
excellent examples of why your suggestion fails: the principle of "Don't
Repeat Yourself".

In Python today, any() and all() will work perfectly on ANY ITERABLE OBJECT,
for free. The developer of that object doesn't need to do anything to
support any() and all(), all she has to do is make it iterable.

Under your suggestion, every iterable object has to implement an any()
method, and an all() method. Every iterable type has to repeat the same old
code as every other iterable type.

class list:
def any(self):
for x in self:
if x: return True
return False

class dict:
def any(self):
for x in self:
if x: return True
return False

class set:
def any(self):
for x in self:
if x: return True
return False

class MyFancyIterableThatIsRealCool:
# Wow, deja vu...
def any(self):
for x in self:
if x: return True
return False


See all the pointlessly duplicated code? Now each one needs tests, and
documentation, and the amount of duplication goes through the roof.

Now, a developer of merely average intelligence will see all that duplicated
code, and factor it out into a global function (two actually, one for
any(), one for all()):

def any(iterable):
# Write this once. Test it once. Document it once.
for x in iterable:
if x: return True
return False

class list:
# Gotta have a method, or Rick will cry.
def any(self):
return any(self)

class dict:
def any(self):
return any(self)

class set:
def any(self):
return any(self)

class MyFancyIterableThatIsRealCool:
# This code seems trivial, and familiar...
def any(self):
return any(self)


But a developer of above average intelligence will recognise that all those
x.any() boilerplate methods are *pointless and stupid*, since you have a
function that does everything you need, for every possible iterator, for
free. All you need do is use any(obj) syntax instead of obj.any() syntax,
which also saves one keystroke.

And a *really* smart language designer will have realised this ahead of
time, and designed the language to encourage the use of protocols like
this, instead of insisting on the slavish application of obj.method syntax.




--
Steven

MRAB

unread,
Feb 8, 2013, 12:14:23 PM2/8/13
to pytho...@python.org
On 2013-02-08 07:22, Steven D'Aprano wrote:
> Rick Johnson wrote:
>
>> Why even have a damn bool function if you're never going to use it?
>
> bool is for converting arbitrary objects into a canonical True or False
> flag. E.g. one use-case is if you wish to record in permanent storage a
> flag, and don't want arbitrary (possibly expensive) objects to be recorded.
>
> Most of the time, you shouldn't care whether you have a canonical True/False
> bool, you should only care whether you have something which duck-types as a
> boolean flag: a truthy or falsey value. In Python, all objects duck-type as
> flags. The usual interpretation is whether the object represents something
> or nothing:
>
> "nothing", or falsey values: None, False, 0, 0.0, '', [], {}, set(), etc.
> (essentially, the empty value for whichever type you are considering)
>
> "something", or truthy values: True, 1, 2.5, 'hello world', etc.
> (essentially, non-empty values).
>
Anything that's not falsey is truey.

> Prior to Python 3, the special method __bool__ was spelled __nonempty__,
> which demonstrates Python's philosophy towards duck-typing bools.
>
Incorrect, it was spelled __nonzero__.

Rick Johnson

unread,
Feb 8, 2013, 12:48:43 PM2/8/13
to
On Friday, February 8, 2013 9:16:42 AM UTC-6, Steven D'Aprano wrote:
> Rick Johnson wrote:
>
> > GvR has always been reluctant to incorporate full OOP machinery for some
> > reason.
>
> Python is a fully object oriented language. It is *more* object oriented
> than, say, Java.

Oh really? *chuckles*

> - everything in Python is an object, there is no distinction between "boxed"
> and "unboxed" variables;

Just because /everything/ in Python is an object does not mean that Python is 100% OOP. This fact is just one of the many attributes of a 100% OOP language. Yes, Python allows OOP style, but python is NOT 100% OOP! Ruby on the other hand /is/ 100% OOP. Although it has identity issues like Python. Ruby thinks it's multi-paridigm and Python thinks it's a good example of OOP. Neither are correct.

> - modules are objects;
>
> - functions and methods are objects;
>
> - classes are objects in Python, and have their own class (the metaclass);
>
> - metaclasses themselves are also objects, and have classes of their own;
>
> - it's objects all the way down, at least until you reach "type" itself,
> which is bootstrapped into existence by the compiler.
>
>
> Although Python is fully object-oriented, it does not insist on one
> particular style of object syntax. It allows procedural and functional
> style syntax as well.

Well you just defeated yourself. How can Python be 100% OOP and then allow other paradigms?

> > I am not suggesting that Python be 100% OOP, HELL NO! But
> > collections should have had an "isempty" method from the beginning. But
> > the same argument could be made against len, any, all, etc...
>
> No they shouldn't.
>
> [...]
>
> Python functions operate as *protocols*. any() and all(), for example, are
> excellent examples of why your suggestion fails: the principle of "Don't
> Repeat Yourself".
>
> In Python today, any() and all() will work perfectly on ANY ITERABLE OBJECT,
> for free. The developer of that object doesn't need to do anything to
> support any() and all(), all she has to do is make it iterable.
>
> Under your suggestion, every iterable object has to implement an any()
> method, and an all() method. Every iterable type has to repeat the same old
> code as every other iterable type.

NOT IF PYTHON WERE TRULY 100% OOP!

If so, Python would have a supertype called "Collection" that wold define all methods that operate on collections. Some of these include:

len, any, all, length, isempty, __getitem__, __setitem__, etc...

Then any collection subtype would inherit from this supertype and get the methods for free.

>
> [...]
> See all the pointlessly duplicated code? Now each one needs tests, and
> documentation, and the amount of duplication goes through the roof.
>
> Now, a developer of merely average intelligence will see all that duplicated
> code, and factor it out into a global function (two actually, one for
> any(), one for all()):

Only if that developer does not understand sub-typing! All he has to do is write the method ONE TIME in a super-type, and then inherit the method into ANY number of sub-types for free. Now, if he wants to pervert the usage of a method to fit some niche, THEN he will need to overload the method and provide proper return value.

> But a developer of above average intelligence will recognise that all those
> x.any() boilerplate methods are *pointless and stupid*, since you have a
> function that does everything you need, for every possible iterator, for
> free. All you need do is use any(obj) syntax instead of obj.any() syntax,
> which also saves one keystroke.
>
> And a *really* smart language designer will have realised this ahead of
> time, and designed the language to encourage the use of protocols like
> this, instead of insisting on the slavish application of obj.method syntax.

Using built-in functions to operate on objects is foolish because you are placing extra burden on the programmer to know which /functions/ work with which /types/. The *only* functions that should be global are the kind that will work on *ANY* object. But then again, the Object type could hold these methods!

len, all, and any (just to name a few) only work for collections types and as such should be methods of these types. The global functions:

sum, len, any, all, enumerate, map, max, min, reversed, sorted, zip

can only be applied to sequence types, or subtypes of a sequence type. So using a /real/ OOP paridigm we would do the following:

## START TRUE OOP PARIDIGM ##

class Object(SuperType):
def __class__
def __delattr__
def __doc__
def __format__
def __getattribute__
def __init__
def __new__
def __repr__
def __setattr__
def __sizeof__
def __str__
def __subclasshook__
def true? # aka: bool
def callable?
def compare(other)
def dir
def hash
def help
def id
def isinstance?(Type)
def issubclass?(Type)
def super
def type

class SequenceBase(Object):
# Methods from object are free
def __add__
def __contains__
def __delattr__
def __delitem__
def __delslice__
def __eq__
def __ge__
def __getitem__
def __getslice__
def __gt__
def __iadd__
def __imul__
def __iter__
def __le__
def __lt__
def __mul__
def __ne__
def __reduce_ex__
def __rmul__
def __setitem__
def __setslice__
def __subclasshook__
#
# Interface
#
def iterator
def length
def sum
def any
def all
def enumerate
def filter(proc)
def frozenset
def map
def max
def min
def reverse
def reduce(proc)
def slice
def sort
def zip


class MySequencyThing(SequenceBase):
# do something here


class List(SequenceBase):
# Methods from SequenceBase and Object are free!
#
# Interface
#
def append
def count
def extend
def index
def insert
def pop
def remove
def reverse
def sort


class MyListyThing(List):
# do something here

## END TRUE OOP PARIDIGM ##

You see, 100% OOP uses encapsulation, inheritance, sub-typing, etc, etc... But most fundamental to OOP is interface (methods belonging to objects), not global functions applied to objects in some haphazard fashion that some self-appointed dictator pull from his backside due to his fear of true OOP style!

Python is not 100% OOP. Heck you cannot fairly apply a specific percentage level because Python's level of OOP is defined by each user of the language. The best way to describe Python is as promiscuous language who secretly longs to be 100% OOP, and to fulfill this fantasy it cross-dresses in OOP lingerie on the weekends.

Roy Smith

unread,
Feb 8, 2013, 12:58:52 PM2/8/13
to
In article <ee71b775-b527-4bb3...@googlegroups.com>,
Rick Johnson <rantingri...@gmail.com> wrote:

> The best way to describe Python is as promiscuous language who secretly
> longs to be 100% OOP, and to fulfill this fantasy it cross-dresses in OOP
> lingerie on the weekends.

+1 QOTD :-)

Rick Johnson

unread,
Feb 8, 2013, 2:58:48 PM2/8/13
to
On Friday, February 8, 2013 11:48:43 AM UTC-6, Rick Johnson wrote:
>
> [...]
>
> So using a /real/ OOP paridigm we would do the following:
>
> ## START TRUE OOP PARIDIGM ##
>
> [...snip naive example...]

Actually my example API is littered with artifacts of a python "global function architecture". In a /true/ 100% OOP language most of these "dunder" methods would become interface members of the object.

There is also the question of WHEN to use and WHEN NOT to use the "dunder" naming convention. I actually like the convention myself for clearly defining methods that are called by "syntactic sugar". However, python employs the convention quite haphazardly.

For example:
__iadd__ is called by an expression such as: "1+=1"
which is translated into: "1.__iadd__(1)"

However:
__repr__ is called by the the global "repr" function
which is translated into: "obj.__repr__()"

I don't like the second usage because i believe this naming convention should be reserved for syntactical sugar only. But i digress...

Here is a better example of Python converted into true OOP paridigm (renaming and removing methods appropriately to fit my logical 100% OOP API).

class Object(SuperType):
def construct # aka: __init__
def desconstruct # aka: __del__
def class
def delattr(name)
def doc
def getattr(name)
def __new__ # dunder???
def repr
def setattr(name, value)
def size
def stringify # aka: __str__
def subclasshook # XXX: dunder???
def true? # aka: __bool__
def callable?
def compare(other)
def methods
def instance_methods
def hash
def help
def id
def isinstance?(this)
def issubclass?(this)
def super
def type

class SequenceBase(Object):
# Methods from object are free
def __add__
def __contains?__
def __delitem__
def __delslice__
def __eq__
def __ge__
def __getitem__
def __getslice__
def __gt__
def __iadd__
def __imul__
def __iter__
def __le__
def __lt__
def __mul__
def __ne__
def __rmul__
def __setitem__
def __setslice__
#
# Interface
#
slice = __getslice__
extend = __add__
contains? = __contains?__
def length # pka: __len__
def any
def all
def enumerate -> iterator
def filter(proc)
def map(proc)
def max
def min
def reverse
def reduce(proc)
def sort
def zip


class Sequence(SequenceBase): # aka: list
# Methods from SequenceBase and Object are free!
#
# Interface
#
def append(this)
def count(this)
def index(this)
def insert(idx, this)
def pop()
def remove(this)
def reverse
def sort

I'm a bit unnerved by the sum function. Summing a sequence only makes sense if the sequence in question contains /only/ numeric types. For that reason i decided to create a special type for holding Numerics. This will probably result in many complaints from lazy people who want to use only one Sequence type, which holds mixed types, THEN jamb nothing but numeric types into it, THEN have a sum method that throws errors when it encounters a non-numeric type!!! I say, too bad for you.

Stop obfuscating your code! Of course someone could probably find a legitimate reason to apply a sum method to non-numeric values; if so, then inherit from NumericSequence and create your custom type!

class NumericSequence(Sequence):
# Methods from Sequence, SequenceBase, and Object are free!
def __setitem__(item):
if not item.isinstance(Numeric):
raise TypeError()
def __add__(other):
if not other.isinstance(NumericSequence):
raise TypeError()
def __setslice__(other):
# blah
#
# Interface
#
def sum -> Integer

Steven D'Aprano

unread,
Feb 8, 2013, 5:18:12 PM2/8/13
to
Oops, so it was. Sorry for the brain-fart.

__nonzero__ or not, nevertheless the implication still applies: all types
are meant to map to "nothing" (zero) or "not nothing" (non-zero).



--
Steven

Chris Angelico

unread,
Feb 8, 2013, 7:05:54 PM2/8/13
to pytho...@python.org
On Sat, Feb 9, 2013 at 6:58 AM, Rick Johnson
<rantingri...@gmail.com> wrote:
> I'm a bit unnerved by the sum function. Summing a sequence only makes sense if the sequence in question contains /only/ numeric types. For that reason i decided to create a special type for holding Numerics. This will probably result in many complaints from lazy people who want to use only one Sequence type, which holds mixed types, THEN jamb nothing but numeric types into it, THEN have a sum method that throws errors when it encounters a non-numeric type!!! I say, too bad for you.


Most assuredly not. The sum builtin works happily on any sequence of
objects that can be added together. It works as an excellent flatten()
method:

>>> nested_list = [["q"], ["w","e"], ["r","t","u"], ["i","o","p"]]
>>> sum(nested_list,[])
['q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p']
>>> nested_list
[['q'], ['w', 'e'], ['r', 't', 'u'], ['i', 'o', 'p']]

I'm not sure what your definition of a numeric type is, but I suspect
that list(str) isn't part of it.

ChrisA

Rick Johnson

unread,
Feb 8, 2013, 7:49:02 PM2/8/13
to pytho...@python.org
On Friday, February 8, 2013 6:05:54 PM UTC-6, Chris Angelico wrote:
> The sum builtin works happily on any sequence of objects
> that can be added together. It works as an excellent
> flatten() method:
>
> >>> nested_list = [["q"], ["w","e"], ["r","t","u"], ["i","o","p"]]
> >>> sum(nested_list,[])
> ['q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p']
> >>> nested_list
> [['q'], ['w', 'e'], ['r', 't', 'u'], ['i', 'o', 'p']]

What the hell? Oh yeah, you must be using pike again. No, if it were pike the list would look like this:

({({"q"}), ({"w","e"}), ({"r","t","u"}), ({"i","o","p"})})

Of course you'd have to declare it first using an /expanded/ Java syntax:

nested_list = array(array(string))

Folks, i couldn't make this stuff up if i wanted to. Go read for yourself if want a few laughs.

http://pike.lysator.liu.se/docs/tutorial/data_types/container_types.xml

> I'm not sure what your definition of a numeric type is, but I suspect
> that list(str) isn't part of it.

Of course not.

Rick Johnson

unread,
Feb 8, 2013, 7:49:02 PM2/8/13
to comp.lan...@googlegroups.com, pytho...@python.org
On Friday, February 8, 2013 6:05:54 PM UTC-6, Chris Angelico wrote:
> The sum builtin works happily on any sequence of objects
> that can be added together. It works as an excellent
> flatten() method:
>
> >>> nested_list = [["q"], ["w","e"], ["r","t","u"], ["i","o","p"]]
> >>> sum(nested_list,[])
> ['q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p']
> >>> nested_list
> [['q'], ['w', 'e'], ['r', 't', 'u'], ['i', 'o', 'p']]

What the hell? Oh yeah, you must be using pike again. No, if it were pike the list would look like this:

({({"q"}), ({"w","e"}), ({"r","t","u"}), ({"i","o","p"})})

Of course you'd have to declare it first using an /expanded/ Java syntax:

nested_list = array(array(string))

Folks, i couldn't make this stuff up if i wanted to. Go read for yourself if want a few laughs.

http://pike.lysator.liu.se/docs/tutorial/data_types/container_types.xml

> I'm not sure what your definition of a numeric type is, but I suspect
> that list(str) isn't part of it.

Of course not.

Ian Kelly

unread,
Feb 8, 2013, 8:06:34 PM2/8/13
to Rick Johnson, Python
On Fri, Feb 8, 2013 at 12:58 PM, Rick Johnson
<rantingri...@gmail.com> wrote:
> I'm a bit unnerved by the sum function. Summing a sequence only makes sense if the sequence in question contains /only/ numeric types. For that reason i decided to create a special type for holding Numerics. This will probably result in many complaints from lazy people who want to use only one Sequence type, which holds mixed types, THEN jamb nothing but numeric types into it, THEN have a sum method that throws errors when it encounters a non-numeric type!!! I say, too bad for you.
>
> Stop obfuscating your code! Of course someone could probably find a legitimate reason to apply a sum method to non-numeric values; if so, then inherit from NumericSequence and create your custom type!

Are you aware that the approach you're advocating here is bad OOP
design? If you declare a class NumericSequence with the property that
it contains only numeric types, and then you declare a subclass
NonnumericSequence that does not share that property, then guess what?
You've just violated the Liskov Substitution Principle.

The goal you're trying to achieve here is nonsensical anyway. Ask
yourself what the semantic meaning of the sum() function is, what
purpose it is meant to serve. My answer: it is the reduction of the
addition operator. The implication of this is that the input type of
the sum() function is not "numbers", but rather "things that can be
added". That includes numbers, but since I see from your proposed
class hierarchy that you are retaining the __add__ method on
sequences, it also includes sequences. Are you really going to tell
the user that (1, 2, 3) + (4, 5, 6) is perfectly fine, but that the
semantic equivalent sum([(1, 2, 3), (4, 5, 6)]) is nonsense?
It is loading more messages.
0 new messages