[Python-checkins] bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975)

Raymond Hettinger webhook-mailer at python.org
Sat Jul 27 17:04:46 EDT 2019


https://github.com/python/cpython/commit/6b5f1b496f0b20144592b640b9c975df43a29eb0
commit: 6b5f1b496f0b20144592b640b9c975df43a29eb0
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-07-27T14:04:29-07:00
summary:

bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975)

files:
A Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst
M Doc/library/math.rst
M Lib/test/test_math.py
M Modules/clinic/mathmodule.c.h
M Modules/mathmodule.c

diff --git a/Doc/library/math.rst b/Doc/library/math.rst
index be953cfe9599..43eaba935a16 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -400,7 +400,8 @@ Trigonometric functions
 .. function:: dist(p, q)
 
    Return the Euclidean distance between two points *p* and *q*, each
-   given as a tuple of coordinates.  The two tuples must be the same size.
+   given as a sequence (or iterable) of coordinates.  The two points
+   must have the same dimension.
 
    Roughly equivalent to::
 
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index 567a5c694c15..c237bc1942e6 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -833,6 +833,10 @@ def testDist(self):
                     sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))
                 )
 
+        # Test non-tuple inputs
+        self.assertEqual(dist([1.0, 2.0, 3.0], [4.0, 2.0, -1.0]), 5.0)
+        self.assertEqual(dist(iter([1.0, 2.0, 3.0]), iter([4.0, 2.0, -1.0])), 5.0)
+
         # Test allowable types (those with __float__)
         self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0)
         self.assertEqual(dist((14, 1), (2, -4)), 13)
@@ -873,8 +877,6 @@ class T(tuple):
             dist((1, 2, 3), (4, 5, 6), (7, 8, 9))
         with self.assertRaises(TypeError):         # Scalars not allowed
             dist(1, 2)
-        with self.assertRaises(TypeError):         # Lists not allowed
-            dist([1, 2, 3], [4, 5, 6])
         with self.assertRaises(TypeError):         # Reject values without __float__
             dist((1.1, 'string', 2.2), (1, 2, 3))
         with self.assertRaises(ValueError):        # Check dimension agree
diff --git a/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst
new file mode 100644
index 000000000000..048478c008e8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst
@@ -0,0 +1,2 @@
+Let math.dist() accept coordinates as sequences (or iterables) rather than
+just tuples.
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index 966b99b6a369..84561b955787 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -297,8 +297,8 @@ PyDoc_STRVAR(math_dist__doc__,
 "\n"
 "Return the Euclidean distance between two points p and q.\n"
 "\n"
-"The points should be specified as tuples of coordinates.\n"
-"Both tuples must be the same size.\n"
+"The points should be specified as sequences (or iterables) of\n"
+"coordinates.  Both inputs must have the same dimension.\n"
 "\n"
 "Roughly equivalent to:\n"
 "    sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))");
@@ -319,15 +319,7 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
     if (!_PyArg_CheckPositional("dist", nargs, 2, 2)) {
         goto exit;
     }
-    if (!PyTuple_Check(args[0])) {
-        _PyArg_BadArgument("dist", 1, "tuple", args[0]);
-        goto exit;
-    }
     p = args[0];
-    if (!PyTuple_Check(args[1])) {
-        _PyArg_BadArgument("dist", 2, "tuple", args[1]);
-        goto exit;
-    }
     q = args[1];
     return_value = math_dist_impl(module, p, q);
 
@@ -720,4 +712,4 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=0eb1e76a769cdd30 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f93cfe13ab2fdb4e input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 4c1dbbe15ecc..e1b46ec384a3 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2427,14 +2427,14 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
 /*[clinic input]
 math.dist
 
-    p: object(subclass_of='&PyTuple_Type')
-    q: object(subclass_of='&PyTuple_Type')
+    p: object
+    q: object
     /
 
 Return the Euclidean distance between two points p and q.
 
-The points should be specified as tuples of coordinates.
-Both tuples must be the same size.
+The points should be specified as sequences (or iterables) of
+coordinates.  Both inputs must have the same dimension.
 
 Roughly equivalent to:
     sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))
@@ -2442,16 +2442,34 @@ Roughly equivalent to:
 
 static PyObject *
 math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
-/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/
+/*[clinic end generated code: output=56bd9538d06bbcfe input=74e85e1b6092e68e]*/
 {
     PyObject *item;
     double max = 0.0;
     double x, px, qx, result;
     Py_ssize_t i, m, n;
-    int found_nan = 0;
+    int found_nan = 0, p_allocated = 0, q_allocated = 0;
     double diffs_on_stack[NUM_STACK_ELEMS];
     double *diffs = diffs_on_stack;
 
+    if (!PyTuple_Check(p)) {
+        p = PySequence_Tuple(p);
+        if (p == NULL) {
+            return NULL;
+        }
+        p_allocated = 1;
+    }
+    if (!PyTuple_Check(q)) {
+        q = PySequence_Tuple(q);
+        if (q == NULL) {
+            if (p_allocated) {
+                Py_DECREF(p);
+            }
+            return NULL;
+        }
+        q_allocated = 1;
+    }
+
     m = PyTuple_GET_SIZE(p);
     n = PyTuple_GET_SIZE(q);
     if (m != n) {
@@ -2482,12 +2500,24 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
     if (diffs != diffs_on_stack) {
         PyObject_Free(diffs);
     }
+    if (p_allocated) {
+        Py_DECREF(p);
+    }
+    if (q_allocated) {
+        Py_DECREF(q);
+    }
     return PyFloat_FromDouble(result);
 
   error_exit:
     if (diffs != diffs_on_stack) {
         PyObject_Free(diffs);
     }
+    if (p_allocated) {
+        Py_DECREF(p);
+    }
+    if (q_allocated) {
+        Py_DECREF(q);
+    }
     return NULL;
 }
 



More information about the Python-checkins mailing list