[Tutor] How is "set(ls).add('a') evaluated? [Was: Re: A program that can check if all elements of the list are mutually disjoint]

Mats Wichmann mats at wichmann.us
Sun Jun 6 18:48:07 EDT 2021


On 6/6/21 3:27 PM, Dennis Lee Bieber wrote:

> 	Consider the difference between list.sort() and sorted(list). The first
> sorts in place and returns None, the second returns a sorted copy of list.

Probably flogging a horse that has already left the barn, or whatever, 
but...

For those who don't like this behavior, maybe consider:

if you ask to _modify_ an object, the result is either that the object 
was modified as you requested, or an exception is raised indicating why 
that couldn't happen. If you didn't get an exception, it succeeded, so 
there's not really anything else Python needs to tell you.

 >>> x = (1, 2)
 >>> x.append(3)
Traceback (most recent call last):
   File "<input>", line 1, in <module>
     x.append(3)
AttributeError: 'tuple' object has no attribute 'append'
 >>> y = [1, 2]
 >>> y.append(3)
 >>>    # silence is golden?

One could have picked something arbitrary to return here, like the new 
size of the object, but why? If you want that, it's easy to ask for.

This is nice and consistent. I realize keeping track of "we are 
modifying an object in place" (no return) vs. "we are asking for a new 
object to be produced" (return the object) is a bit of a pain. And I do 
know this has led to errors - I've made more than a few of this type myself.

When you write your own code, I'm becoming more and more fond of adding 
one specific kind of type annotation: the return type. This has two 
useful effects:

1. You find you're having a really hard time describing the return type, 
and you reach for Any, or for a really complex Union, or...  this 
probably means you've written a function that isn't single purpose 
enough, and you should stop and think how all the different kinds of 
returns are likely to confuse users of your code.

2. You identify the cases where you return "something-useful" or None. 
Which can be a real pain for users.  You tell them they'll get back a 
certain object, so they perform an operation on the object, and get what 
I think is currently one of the most frequent Python errors:

AttributeError: 'NoneType' object has no attribute 'index'

... because there was a path where you didn't actually return one of 
those expected objects.  I know sentinels can be useful if all the 
values of an object are valid values (e.g. you want to return a string, 
and "" is also a valid value), but maybe in some circumstances an 
exception might be a better thing to do for the case where there wasn't 
a valid value to return?

In any case, whichever approach you think is right, a checker will let 
you know you're returning something that's not of an expected type if you:

def foo(a, b) -> str:
       # buncha code
       if ok:
           return obj

       return None

so you can at least be reminded to put it in the docs and/or revise the 
return-type annotation to show None is possible.



More information about the Tutor mailing list