Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 66 additions & 9 deletions Doc/library/itertools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
from collections import Counter, deque
from contextlib import suppress
from functools import reduce
from heapq import heappush, heappushpop, heappush_max, heappushpop_max
from math import comb, isqrt, prod, sumprod
from operator import getitem, is_not, itemgetter, mul, neg, truediv

Expand All @@ -848,11 +849,6 @@ and :term:`generators <generator>` which incur interpreter overhead.
# prepend(1, [2, 3, 4]) → 1 2 3 4
return chain([value], iterable)

def running_mean(iterable):
"Yield the average of all values seen so far."
# running_mean([8.5, 9.5, 7.5, 6.5]) → 8.5 9.0 8.5 8.0
return map(truediv, accumulate(iterable), count(1))

def repeatfunc(function, times=None, *args):
"Repeat calls to a function with specified arguments."
if times is None:
Expand Down Expand Up @@ -1150,6 +1146,49 @@ and :term:`generators <generator>` which incur interpreter overhead.
return n


# ==== Running statistics ====

def running_mean(iterable):
"Average of values seen so far."
# running_mean([37, 33, 38, 28]) → 37 35 36 34
return map(truediv, accumulate(iterable), count(1))

def running_min(iterable):
"Smallest of values seen so far."
# running_min([37, 33, 38, 28]) → 37 33 33 28
return accumulate(iterable, func=min)

def running_max(iterable):
"Largest of values seen so far."
# running_max([37, 33, 38, 28]) → 37 37 38 38
return accumulate(iterable, func=max)

def running_median(iterable):
"Median of values seen so far."
# running_median([37, 33, 38, 28]) → 37 35 37 35
read = iter(iterable).__next__
lo = [] # max-heap
hi = [] # min-heap the same size as or one smaller than lo
with suppress(StopIteration):
while True:
heappush_max(lo, heappushpop(hi, read()))
yield lo[0]
heappush(hi, heappushpop_max(lo, read()))
yield (lo[0] + hi[0]) / 2

def running_statistics(iterable):
"Aggregate statistics for values seen so far."
# Generate tuples: (size, minimum, median, maximum, mean)
t0, t1, t2, t3 = tee(iterable, 4)
return zip(
count(1),
running_min(t0),
running_median(t1),
running_max(t2),
running_mean(t3),
)


.. doctest::
:hide:

Expand Down Expand Up @@ -1226,10 +1265,6 @@ and :term:`generators <generator>` which incur interpreter overhead.
[(0, 'a'), (1, 'b'), (2, 'c')]


>>> list(running_mean([8.5, 9.5, 7.5, 6.5]))
[8.5, 9.0, 8.5, 8.0]


>>> for _ in loops(5):
... print('hi')
...
Expand Down Expand Up @@ -1789,6 +1824,28 @@ and :term:`generators <generator>` which incur interpreter overhead.
True


>>> list(running_mean([8.5, 9.5, 7.5, 6.5]))
[8.5, 9.0, 8.5, 8.0]
>>> list(running_mean([37, 33, 38, 28]))
[37.0, 35.0, 36.0, 34.0]


>>> list(running_min([37, 33, 38, 28]))
[37, 33, 33, 28]


>>> list(running_max([37, 33, 38, 28]))
[37, 37, 38, 38]


>>> list(running_median([37, 33, 38, 28]))
[37, 35.0, 37, 35.0]


>>> list(running_statistics([37, 33, 38, 28]))
[(1, 37, 37, 37, 37.0), (2, 33, 35.0, 37, 35.0), (3, 33, 37, 38, 36.0), (4, 28, 35.0, 38, 34.0)]


.. testcode::
:hide:

Expand Down
44 changes: 43 additions & 1 deletion Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1980,7 +1980,7 @@ without the dedicated syntax, as documented below.

.. _typevartuple:

.. class:: TypeVarTuple(name, *, default=typing.NoDefault)
.. class:: TypeVarTuple(name, *, bound=None, covariant=False, contravariant=False, infer_variance=False, default=typing.NoDefault)

Type variable tuple. A specialized form of :ref:`type variable <typevar>`
that enables *variadic* generics.
Expand Down Expand Up @@ -2090,6 +2090,24 @@ without the dedicated syntax, as documented below.

The name of the type variable tuple.

.. attribute:: __covariant__

Whether the type variable tuple has been explicitly marked as covariant.

.. versionadded:: 3.15

.. attribute:: __contravariant__

Whether the type variable tuple has been explicitly marked as contravariant.

.. versionadded:: 3.15

.. attribute:: __infer_variance__

Whether the type variable tuple's variance should be inferred by type checkers.

.. versionadded:: 3.15

.. attribute:: __default__

The default value of the type variable tuple, or :data:`typing.NoDefault` if it
Expand All @@ -2116,6 +2134,11 @@ without the dedicated syntax, as documented below.

.. versionadded:: 3.13

Type variable tuples created with ``covariant=True`` or
``contravariant=True`` can be used to declare covariant or contravariant
generic types. The ``bound`` argument is also accepted, similar to
:class:`TypeVar`, but its actual semantics are yet to be decided.

.. versionadded:: 3.11

.. versionchanged:: 3.12
Expand All @@ -2127,6 +2150,11 @@ without the dedicated syntax, as documented below.

Support for default values was added.

.. versionchanged:: 3.15

Added support for the ``bound``, ``covariant``, ``contravariant``, and
``infer_variance`` parameters.

.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False, default=typing.NoDefault)

Parameter specification variable. A specialized version of
Expand Down Expand Up @@ -2196,6 +2224,20 @@ without the dedicated syntax, as documented below.

The name of the parameter specification.

.. attribute:: __covariant__

Whether the parameter specification has been explicitly marked as covariant.

.. attribute:: __contravariant__

Whether the parameter specification has been explicitly marked as contravariant.

.. attribute:: __infer_variance__

Whether the parameter specification's variance should be inferred by type checkers.

.. versionadded:: 3.12

.. attribute:: __default__

The default value of the parameter specification, or :data:`typing.NoDefault` if it
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,11 @@ typing
child classes of that class cannot inherit from other disjoint bases that are
not parent or child classes of ``C``. (Contributed by Jelle Zijlstra in :gh:`148639`.)

* :class:`~typing.TypeVarTuple` now accepts ``bound``, ``covariant``,
``contravariant``, and ``infer_variance`` keyword arguments, matching the
interface of :class:`~typing.TypeVar` and :class:`~typing.ParamSpec`.
``bound`` semantics remain undefined in the specification.


unicodedata
-----------
Expand Down
95 changes: 73 additions & 22 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ def test_typevartuple_none(self):
self.assertIs(U_None.__default__, None)
self.assertIs(U_None.has_default(), True)

class X[**Ts]: ...
class X[*Ts]: ...
Ts, = X.__type_params__
self.assertIs(Ts.__default__, NoDefault)
self.assertIs(Ts.has_default(), False)
Expand Down Expand Up @@ -1288,6 +1288,57 @@ def test_cannot_call_instance(self):
with self.assertRaises(TypeError):
Ts()

def test_default_variance(self):
Ts = TypeVarTuple('Ts')
self.assertIs(Ts.__covariant__, False)
self.assertIs(Ts.__contravariant__, False)
self.assertIs(Ts.__infer_variance__, False)
self.assertIsNone(Ts.__bound__)

def test_covariant(self):
Ts_co = TypeVarTuple('Ts_co', covariant=True)
self.assertIs(Ts_co.__covariant__, True)
self.assertIs(Ts_co.__contravariant__, False)
self.assertIs(Ts_co.__infer_variance__, False)

def test_contravariant(self):
Ts_contra = TypeVarTuple('Ts_contra', contravariant=True)
self.assertIs(Ts_contra.__covariant__, False)
self.assertIs(Ts_contra.__contravariant__, True)
self.assertIs(Ts_contra.__infer_variance__, False)

def test_infer_variance(self):
Ts = TypeVarTuple('Ts', infer_variance=True)
self.assertIs(Ts.__covariant__, False)
self.assertIs(Ts.__contravariant__, False)
self.assertIs(Ts.__infer_variance__, True)

def test_bound(self):
Ts_bound = TypeVarTuple('Ts_bound', bound=int)
self.assertIs(Ts_bound.__bound__, int)
Ts_no_bound = TypeVarTuple('Ts_no_bound')
self.assertIsNone(Ts_no_bound.__bound__)

def test_no_bivariant(self):
with self.assertRaises(ValueError):
TypeVarTuple('Ts', covariant=True, contravariant=True)

def test_cannot_combine_explicit_and_infer(self):
with self.assertRaises(ValueError):
TypeVarTuple('Ts', covariant=True, infer_variance=True)
with self.assertRaises(ValueError):
TypeVarTuple('Ts', contravariant=True, infer_variance=True)

def test_repr_with_variance(self):
Ts = TypeVarTuple('Ts')
self.assertEqual(repr(Ts), '~Ts')
Ts_co = TypeVarTuple('Ts_co', covariant=True)
self.assertEqual(repr(Ts_co), '+Ts_co')
Ts_contra = TypeVarTuple('Ts_contra', contravariant=True)
self.assertEqual(repr(Ts_contra), '-Ts_contra')
Ts_infer = TypeVarTuple('Ts_infer', infer_variance=True)
self.assertEqual(repr(Ts_infer), 'Ts_infer')

def test_unpacked_typevartuple_is_equal_to_itself(self):
Ts = TypeVarTuple('Ts')
self.assertEqual((*Ts,)[0], (*Ts,)[0])
Expand Down Expand Up @@ -1427,16 +1478,16 @@ def test_repr_is_correct(self):
class G1(Generic[*Ts]): pass
class G2(Generic[Unpack[Ts]]): pass

self.assertEqual(repr(Ts), 'Ts')
self.assertEqual(repr(Ts), '~Ts')

self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[Ts]')
self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[Ts]')
self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[~Ts]')
self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[~Ts]')

self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[Ts]]')
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[Ts]]')
self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[~Ts]]')
self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[~Ts]]')

self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[Ts]]')
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[Ts]]]')
self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[~Ts]]')
self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[~Ts]]]')

def test_variadic_class_repr_is_correct(self):
Ts = TypeVarTuple('Ts')
Expand Down Expand Up @@ -1475,61 +1526,61 @@ def test_variadic_class_alias_repr_is_correct(self):
class A(Generic[Unpack[Ts]]): pass

B = A[*Ts]
self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]')
self.assertEndsWith(repr(B), 'A[typing.Unpack[~Ts]]')
self.assertEndsWith(repr(B[()]), 'A[()]')
self.assertEndsWith(repr(B[float]), 'A[float]')
self.assertEndsWith(repr(B[float, str]), 'A[float, str]')

C = A[Unpack[Ts]]
self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]')
self.assertEndsWith(repr(C), 'A[typing.Unpack[~Ts]]')
self.assertEndsWith(repr(C[()]), 'A[()]')
self.assertEndsWith(repr(C[float]), 'A[float]')
self.assertEndsWith(repr(C[float, str]), 'A[float, str]')

D = A[*Ts, int]
self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]')
self.assertEndsWith(repr(D), 'A[typing.Unpack[~Ts], int]')
self.assertEndsWith(repr(D[()]), 'A[int]')
self.assertEndsWith(repr(D[float]), 'A[float, int]')
self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]')

E = A[Unpack[Ts], int]
self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]')
self.assertEndsWith(repr(E), 'A[typing.Unpack[~Ts], int]')
self.assertEndsWith(repr(E[()]), 'A[int]')
self.assertEndsWith(repr(E[float]), 'A[float, int]')
self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]')

F = A[int, *Ts]
self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]')
self.assertEndsWith(repr(F), 'A[int, typing.Unpack[~Ts]]')
self.assertEndsWith(repr(F[()]), 'A[int]')
self.assertEndsWith(repr(F[float]), 'A[int, float]')
self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]')

G = A[int, Unpack[Ts]]
self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]')
self.assertEndsWith(repr(G), 'A[int, typing.Unpack[~Ts]]')
self.assertEndsWith(repr(G[()]), 'A[int]')
self.assertEndsWith(repr(G[float]), 'A[int, float]')
self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]')

H = A[int, *Ts, str]
self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]')
self.assertEndsWith(repr(H), 'A[int, typing.Unpack[~Ts], str]')
self.assertEndsWith(repr(H[()]), 'A[int, str]')
self.assertEndsWith(repr(H[float]), 'A[int, float, str]')
self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]')

I = A[int, Unpack[Ts], str]
self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]')
self.assertEndsWith(repr(I), 'A[int, typing.Unpack[~Ts], str]')
self.assertEndsWith(repr(I[()]), 'A[int, str]')
self.assertEndsWith(repr(I[float]), 'A[int, float, str]')
self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]')

J = A[*Ts, *tuple[str, ...]]
self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]')
self.assertEndsWith(repr(J), 'A[typing.Unpack[~Ts], *tuple[str, ...]]')
self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]')
self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]')
self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]')

K = A[Unpack[Ts], Unpack[Tuple[str, ...]]]
self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]')
self.assertEndsWith(repr(K), 'A[typing.Unpack[~Ts], typing.Unpack[typing.Tuple[str, ...]]]')
self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]')
self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]')
self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]')
Expand All @@ -1550,9 +1601,9 @@ class G(type(Unpack[Ts])): pass
with self.assertRaisesRegex(TypeError,
r'Cannot subclass typing\.Unpack'):
class H(Unpack): pass
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[~Ts\]'):
class I(*Ts): pass
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'):
with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[~Ts\]'):
class J(Unpack[Ts]): pass

def test_variadic_class_args_are_correct(self):
Expand Down Expand Up @@ -5596,13 +5647,13 @@ class TsP(Generic[*Ts, P]):
MyCallable[[int], bool]: "MyCallable[[int], bool]",
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[Ts], ~P], ~T]",
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[~Ts], ~P], ~T]",

DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",

TsP[*Ts, P]: "TsP[typing.Unpack[Ts], ~P]",
TsP[*Ts, P]: "TsP[typing.Unpack[~Ts], ~P]",
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:class:`typing.TypeVarTuple` now accepts ``bound``, ``covariant``,
``contravariant``, and ``infer_variance`` parameters, matching the interface
of :class:`typing.TypeVar` and :class:`typing.ParamSpec`.
Loading
Loading