[Michael Cuthbert:]
> > I'm rather surprised though that the typing advantages of the pep have not 
> > been emphasized enough.

[Chris Angelico]
> That would be because I personally don't use that kind of strict
> typing, so it's not something I'm really qualified to talk about.
> Would you like to write up a paragraph or two about it? I could
> incorporate it verbatim, if you like.

Sure!

* * *
Late-bound arg defaults also help with proper typing, especially for 
established code-bases that are incrementally adding typing to their code.  For 
instance, take this untyped example that might take a list of musical pitches 
and put them in a particular order:

def order_pitches(pitches=None):
     if pitches is None:
         pitches = []
     ...  # do reordering referencing "pitches" a lot
     pitches.sort(key=lambda p: (p.octave, p.name))
     return pitches

When the editor moves to a typed version, something like this would seem 
reasonable:

def order_pitches(pitches: list[Pitch]|None = None) -> list[Pitch]:
      ...  # same code as above.

However, some type checkers (for instance, that of PyCharm 2022) will continue 
to reveal the type as "list[Pitch]|None" even after type narrowing.  (Mypy will 
correctly type narrow in this example but not in code that checks for `if not 
hasattr(pitches, '__iter__')` and in many other more complex examples).

In this case, authors sometimes need to resort to rewriting code with a new 
variable name:

def order_pitches(pitches: list[Pitch]|None = None) -> list[Pitch]:
      non_none_pitches: list[Pitch]
      if pitches is None:
          non_none_pitches = []
     else:
          non_none_pitches = cast(list[Pitch], pitches)
     ...  # rest of code must be rewritten to use "non_none_pitches"

The typed definition also seems to imply that "None" is an acceptable calling 
type for the function rather than just being a stand-in for an omitted call.  A 
type-checker will allow `order_pitches(None)` to pass, perhaps preventing later 
refactoring to use a sentinel such as:

def order_pitches(pitches: list[Pitch]|MISSING = MISSING) -> list[Pitch]:
    if pitches is MISSING:
        pitches = []
    ...

With the PEP, the process of adding typing does not impact the code, nor imply 
that "None" is a fine calling signature.

def order_pitches(pitches: list[Pitch] => []) -> list[Pitch]:
    ...  
    # pitches is always list[Pitch] and needs no narrowing
    pitches.sort(...)
    return pitches
 

*   *   *
It is true that the tools for automatic type-narrowing continue to get better 
(in looking up code that broke the type-narrowing with a "no_none..." variable, 
I found that 2 of 4 places I found where I had done this workaround two years 
ago no longer needed it for either mypy or PyCharm), but there are still 
numerous cases where the process of typing around a sentinel type that will be 
replaced by the correct type for computing still unnecessarily exist.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3MCSLWZUQQTK6FQWTBTCQPUUTGIFKND4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to