[Tutor] beginning to code

Steve D'Aprano steve+python at pearwood.info
Wed Sep 13 22:20:03 EDT 2017


Hi Roger,

My responses below yours, interleaved with your comments.


On Thu, 14 Sep 2017 02:18 am, ROGER GRAYDON CHRISTMAN wrote:

> I have not yet mastered how to respond to a particular note in a threadwith
> the mailer that I use, 

Without knowing your mailer "Penn State WebMail", I would expect that the right
process would be to click on the post and select "Reply To List" (if available)
or "Reply All" (as a fall back).

But seeing that your university appears to have invented their own mailer,
instead of allowing people to use their own, standard, mailer like Outlook or
Thunderbird, I wouldn't be surprised if it were "Aubergine Foxtrot"
or "Xuswbdpemaq".


> so this is not in response to anyone in particular,but 
> just to some of the sentiments as a whole.
>>  if x:>      # do something

While we all make the odd typo or two, it is difficult to understand your
intention when you write something which is meant to be source code but it
contains typos. If you meant what you *literally* wrote, you would get a syntax
error. If you meant

    if x > :

then again you would get a syntax error. If you meant 

    if x > 1: 

(say) then the intent is clear: x is a number being tested to see if it is
greater than 1.

But if you meant simply:

    if x:

then the answer is, you are testing for truthiness. The specific definition
of "truthiness" depends on the type of x, but the general convention in Python
is to distinguish between "something" and "nothing":

Values that in some sense are "nothing" are false:

- zero
- the empty string
- None
- empty containers (lists, dicts, sets, etc.)
- and of course False itself

Values that in some sense are "something" are true:

- nonzero numbers
- nonempty strings
- nonempty containers
- any other object (by default)
- and of course True itself.


True and False are merely the canonical boolean values, they otherwise have no
special status as boolean true/false flags.


> Completely out of context, I would dislike it just because it is far too
> vague.Is it testing for zero?  for an empty list?  or for some object's
> completelyarbitrary definition of truthiness?

"x" is, of course, a poor name for a variable, and I trust that you wouldn't
actually write "if x" in a real program.

(Apart from purely mathematical functions, where x is intended as a real-valued
number, but then you would be unlikely to write "if x" in such a function.)

And of course it is rare that we read a single if clause out of context.
Normally we would have a good idea of what x actually was:


x = list(values)  # terrible choice of names...
if x:  # but at least we can see what x is
    first = x[0]
    last = x[-1]


> x.._bool__() seems to me to be just as efficient, but we don't like calling
> dunders.bool(x) should mean the same thing, perhaps with an extra function
> invocationto look for the dunder.

No. bool() does more than just look up a dunder method and call it.

In general the reason we don't call dunder methods directly is not just for the
aesthetics of it but because dunder methods are implementation, not interface.
The interface can be far more involved than the implementation in the dunder
method. bool is an especially simple example:

# pseudocode for bool(obj)
if hasattr(type(obj), '__bool__'):
    flag = type(obj).__boo__(obj)
    if flag is neither True nor False:
        raise TypeError
elif hasattr(type(obj), '__len__'):
    flag = (type(obj).__len__(obj) != 0)
else:
    flag = True
return flag


The interface for other dunder methods like __add__ and __mul__ are
significantly more complex!

So in general we should not call obj.__bool__, not just because it is ugly or
because it is not idiomatic, but because it is *wrong*.

(There are occasional exceptions. As always, if you know what you are doing, you
know when to break the rules.)


> But even so, nothing here sheds light on what that Boolean outcome means.

*shrug*

This same objection applies to any use of operator overloading or duck-typing.
You are either comfortable with some "theoretical" uncertainly, or you are not.

Without context, we cannot know what:

(width + length)*2

will return, since either or both variable might have overloaded the + operator
to do something weird. The solution is: don't do that.

Truth testing in Python follows the same principles as nearly everything else:

- duck-typing is preferred over type testing (within limits)

- delegate the implementation to the object in question


So rather than insist on an exact True or False object, Python accepts any
object, and interrogates it: "are you truthy or falsey?"



> I would agree that testing any of those for '== True' or the like is
> pointlessredundancy, which may or may not be slower, depending on optimization
> issues.
> ------
> but in context is another thing entirely.
> was x assigned to recently?   is the type of x obvious so its truthiness is
> also obvious?Then fine -- if it is clear that x is a list, I'm happy to use
> this to test for an empty list.But if x is some other creation, like a user
> defined type, I would really prefer to seesomething analogous to:
> if not x.is_empty()     or    x.exists()     or    x.has_brain()
> And most definitely if x is assigned outside my control, I would definitely
> wantsome way to test or verify x's type before I start using it, lest my
> randomnumber generator with its
> (A + B * C) % D
> finds itself concatenating strings and formatting data.

You don't have to protect yourself from the caller's stupidity. You should think
of Python functions having something like "Design By Contract" semantics: if
you pass something that the function is not equipped to handle, then the
(informal) contract is broken and the function can do anything it likes.

Of course we would prefer that the function would (in order of highest to lowest
preference):

- raise a clear TypeError or ValueError stating the failure;

- raise some implementation-dependent exception inside the body of the function;

- return some obviously invalid result;

rather than return something which *looks* valid but is wrong, but don't feel
that it is *necessarily* your responsibility to guard against the caller's
stupidity. "We're all adults here" and if the caller chooses to shoot
themselves in the foot by passing a bad argument, they'll get what's coming to
them.

(Defensive programming is not only the receiving function's responsibility. The
caller should program defensively to ensure they don't pass the wrong arguments
too.)

In fact, there's a fine line between guarding against bad input, and guarding
against input which in fact is perfectly fine, but merely is something you
didn't think of.

(By the way, I really, really hope you aren't actually using such a poor quality
pseudo-random generator when the Python standard library includes a world-class
implementation.)




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list