From 4f9ffc9d1a6a293563deaaaaf4a13331302219b4 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 5 Aug 2019 13:33:19 -0700 Subject: [PATCH] bpo-37759: First round of major edits to Whatsnew 3.8 (GH-15127) --- Doc/whatsnew/3.8.rst | 205 ++++++++++++++---- .../2019-08-04-19-20-58.bpo-37759.EHRF4i.rst | 1 + 2 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2019-08-04-19-20-58.bpo-37759.EHRF4i.rst diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 0455688841f..d8062e772f6 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -42,21 +42,25 @@ This saves the maintainer the effort of going through the Mercurial log when researching a change. -This article explains the new features in Python 3.8, compared to 3.7. +:Editor: Raymond Hettinger +This article explains the new features in Python 3.8, compared to 3.7. For full details, see the :ref:`changelog `. -.. note:: +Prerelease users should be aware that this document is currently in +draft form. It will be updated as Python 3.8 moves towards release, so +it's worth checking back even after reading earlier versions. Some +notable items not yet covered are: - Prerelease users should be aware that this document is currently in draft - form. It will be updated substantially as Python 3.8 moves towards release, - so it's worth checking back even after reading earlier versions. +* :pep:`578` - Runtime audit hooks for potentially sensitive operations +* ``python -m asyncio`` runs a natively async REPL - Some notable items not yet covered here: +.. testsetup:: - * :pep:`578` - Runtime audit hooks for potentially sensitive operations - * ``python -m asyncio`` runs a natively async REPL - * ... + from datetime import date + from math import cos, radians + import re + import math Summary -- Release highlights @@ -76,12 +80,43 @@ New Features Assignment expressions ---------------------- -There is new syntax (the "walrus operator", ``:=``) to assign values -to variables as part of an expression. Example:: +There is new syntax ``:=`` that assigns values to variables as part of a larger +expression. It is affectionately known as "walrus operator" due to +its resemblance to `the eyes and tusks of a walrus +`_. + +In this example, the assignment expression helps avoid calling +:func:`len` twice:: if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)") +A similar benefit arises during regular expression matching where +match objects are needed twice, once to test whether a match +occurred and another to extract a subgroup:: + + discount = 0.0 + if (mo := re.search(r'(\d+)% discount', advertisement)): + discount = float(mo.group(1)) / 100.0 + +The operator is also useful with while-loops that compute +a value to test loop termination and then need that same +value again in the body of the loop:: + + # Loop over fixed length blocks + while (block := f.read(256)) != '': + process(block) + +Another motivating use case arises in list comprehensions where +a value computed in a filtering condition is also needed in +the expression body:: + + [clean_name.title() for name in names + if (clean_name := normalize('NFC', name)) in allowed_names] + +Try to limit use of the walrus operator to clean cases that reduce +complexity and improve readability. + See :pep:`572` for a full description. (Contributed by Emily Morehouse in :issue:`35224`.) @@ -92,20 +127,69 @@ See :pep:`572` for a full description. Positional-only parameters -------------------------- -There is new syntax (``/``) to indicate that some function parameters -must be specified positionally (i.e., cannot be used as keyword -arguments). This is the same notation as shown by ``help()`` for -functions implemented in C (produced by Larry Hastings' "Argument -Clinic" tool). Example:: +There is a new function parameter syntax ``/`` to indicate that some +function parameters must be specified positionally and cannot be used as +keyword arguments. This is the same notation shown by ``help()`` for C +functions annotated with Larry Hastings' `Argument Clinic +`_ tool. + +In the following example, parameters *a* and *b* are positional-only, +while *c* or *d* can be positional or keyword, and *e* or *f* are +required to be keywords:: + + def f(a, b, /, c, d, *, e, f): + print(a, b, c, d, e, f) + +The following is a valid call:: + + f(10, 20, 30, d=40, e=50, f=60) + +However, these are invalid calls:: + + f(10, b=20, c=30, d=40, e=50, f=60) # b cannot be a keyword argument + f(10, 20, 30, 40, 50, f=60) # e must be a keyword argument + +One use case for this notation is that it allows pure Python functions +to fully emulate behaviors of existing C coded functions. For example, +the built-in :func:`pow` function does not accept keyword arguments:: def pow(x, y, z=None, /): - r = x**y - if z is not None: - r %= z - return r + "Emulate the built in pow() function" + r = x ** y + return r if z is None else r%z -Now ``pow(2, 10)`` and ``pow(2, 10, 17)`` are valid calls, but -``pow(x=2, y=10)`` and ``pow(2, 10, z=17)`` are invalid. +Another use case is to preclude keyword arguments when the parameter +name is not helpful. For example, the builtin :func:`len` function has +the signature ``len(obj, /)``. This precludes awkward calls such as:: + + len(obj='hello') # The "obj" keyword argument impairs readability + +A further benefit of marking a parameter as positional-only is that it +allows the parameter name to be changed in the future without risk of +breaking client code. For example, in the :mod:`statistics` module, the +parameter name *dist* may be changed in the future. This was made +possible with the following function specification:: + + def quantiles(dist, /, *, n=4, method='exclusive') + ... + +Since the parameters to the left of ``/`` are not exposed as possible +keywords, the parameters names remain available for use in ``**kwargs``:: + + >>> def f(a, b, /, **kwargs): + ... print(a, b, kwargs) + ... + >>> f(10, 20, a=1, b=2, c=3) # a and b are used in two ways + 10 20 {'a': 1, 'b': 2, 'c': 3} + +This greatly simplifies the implementation of functions and methods +that need to accept arbitrary keyword arguments. For example, here +is an except from code in the :mod:`collections` module:: + + class Counter(dict): + + def __init__(self, iterable=None, /, **kwds): + # Note "iterable" is a possible keyword argument See :pep:`570` for a full description. @@ -174,17 +258,31 @@ Android and Cygwin, whose cases are handled by the script); this change is backward incompatible on purpose. (Contributed by Victor Stinner in :issue:`36721`.) -f-strings now support = for quick and easy debugging ------------------------------------------------------ -Add ``=`` specifier to f-strings. ``f'{expr=}'`` expands -to the text of the expression, an equal sign, then the repr of the -evaluated expression. So:: +f-strings support ``=`` for self-documenting expressions and debugging +---------------------------------------------------------------------- - x = 3 - print(f'{x*9 + 15=}') +Added an ``=`` specifier to :term:`f-string`\s. An f-string such as +``f'{expr=}'`` will expand to the text of the expression, an equal sign, +then the representation of the evaluated expression. For example: -Would print ``x*9 + 15=42``. + >>> user = 'eric_idle' + >>> member_since = date(1975, 7, 31) + >>> f'{user=} {member_since=}' + "user='eric_idle' member_since=datetime.date(1975, 7, 31)" + +The usual :ref:`f-string format specifiers ` allow more +control over how the result of the expression is displayed:: + + >>> delta = date.today() - member_since + >>> f'{user=!s} {delta.days=:,d}' + 'user=eric_idle delta.days=16,075' + +The ``=`` specifier will display the whole expression so that +calculations can be shown:: + + >>> print(f'{theta=} {cos(radians(theta))=:.3f}') + theta=30 cos(radians(theta))=0.866 (Contributed by Eric V. Smith and Larry Hastings in :issue:`36817`.) @@ -295,7 +393,13 @@ Other Language Changes or :meth:`~object.__complex__` is not available. (Contributed by Serhiy Storchaka in :issue:`20092`.) -* Added support of ``\N{name}`` escapes in :mod:`regular expressions `. +* Added support of ``\N{name}`` escapes in :mod:`regular expressions `:: + + >>> notice = 'Copyright © 2019' + >>> copyright_year_pattern = re.compile(r'\N{copyright sign}\s*(\d{4})') + >>> int(copyright_year_pattern.search(notice).group(1)) + 2019 + (Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.) * Dict and dictviews are now iterable in reversed insertion order using @@ -343,10 +447,30 @@ Other Language Changes * Added new ``replace()`` method to the code type (:class:`types.CodeType`). (Contributed by Victor Stinner in :issue:`37032`.) -* For integers, the three-argument form of the :func:`pow` function now permits - the exponent to be negative in the case where the base is relatively prime to - the modulus. It then computes a modular inverse to the base when the exponent - is ``-1``, and a suitable power of that inverse for other negative exponents. +* For integers, the three-argument form of the :func:`pow` function now + permits the exponent to be negative in the case where the base is + relatively prime to the modulus. It then computes a modular inverse to + the base when the exponent is ``-1``, and a suitable power of that + inverse for other negative exponents. For example, to compute the + `modular multiplicative inverse + `_ of 38 + modulo 137, write:: + + >>> pow(38, -1, 137) + 119 + >>> 119 * 38 % 137 + 1 + + Modular inverses arise in the solution of `linear Diophantine + equations `_. + For example, to find integer solutions for ``4258𝑥 + 147𝑦 = 369``, + first rewrite as ``4258𝑥 ≡ 369 (mod 147)`` then solve: + + >>> x = 369 * pow(4258, -1, 147) % 147 + >>> y = (4258 * x - 369) // -147 + >>> 4258 * x + 147 * y + 369 + (Contributed by Mark Dickinson in :issue:`36027`.) * When dictionary comprehensions are evaluated, the key is now evaluated before @@ -576,7 +700,14 @@ Formerly, it only supported the 2-D case. Added new function, :func:`math.prod`, as analogous function to :func:`sum` that returns the product of a 'start' value (default: 1) times an iterable of -numbers. (Contributed by Pablo Galindo in :issue:`35606`) +numbers:: + + >>> prior = 0.8 + >>> likelihoods = [0.625, 0.84, 0.30] + >>> (link: http://math.prod) math.prod(likelihoods, start=prior) + 0.126 + +(Contributed by Pablo Galindo in :issue:`35606`) Added new function :func:`math.isqrt` for computing integer square roots. (Contributed by Mark Dickinson in :issue:`36887`.) @@ -1357,7 +1488,7 @@ Changes in the Python API * :func:`shutil.copyfile` default buffer size on Windows was changed from 16 KiB to 1 MiB. -* ``PyGC_Head`` struct is changed completely. All code touched the +* The ``PyGC_Head`` struct has changed completely. All code that touched the struct member should be rewritten. (See :issue:`33597`) * The ``PyInterpreterState`` struct has been moved into the "internal" diff --git a/Misc/NEWS.d/next/Documentation/2019-08-04-19-20-58.bpo-37759.EHRF4i.rst b/Misc/NEWS.d/next/Documentation/2019-08-04-19-20-58.bpo-37759.EHRF4i.rst new file mode 100644 index 00000000000..90fb7213ebd --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2019-08-04-19-20-58.bpo-37759.EHRF4i.rst @@ -0,0 +1 @@ +Beginning edits to Whatsnew 3.8