summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-07-27 21:26:58 (GMT)
committerRaymond Hettinger <rhettinger@users.noreply.github.com>2019-07-27 21:26:58 (GMT)
commit76821bab9cb77fa7f847e66f8b2309ca30546c7f (patch)
treed7abe932c07b4100e4116aa4d8f840ce2c29a738
parent171019354aa2c717af2e7b2c90aec7b9724f7282 (diff)
downloadcpython-76821bab9cb77fa7f847e66f8b2309ca30546c7f.zip
cpython-76821bab9cb77fa7f847e66f8b2309ca30546c7f.tar.gz
cpython-76821bab9cb77fa7f847e66f8b2309ca30546c7f.tar.bz2
bpo-37691: Let math.dist() accept sequences and iterables for coordinates (GH-14975) (GH-14984)
(cherry picked from commit 6b5f1b496f0b20144592b640b9c975df43a29eb0) Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
-rw-r--r--Doc/library/math.rst3
-rw-r--r--Lib/test/test_math.py6
-rw-r--r--Misc/NEWS.d/next/Library/2019-07-26-22-30-01.bpo-37691.1Li3rx.rst2
-rw-r--r--Modules/clinic/mathmodule.c.h14
-rw-r--r--Modules/mathmodule.c42
5 files changed, 47 insertions, 20 deletions
diff --git a/Doc/library/math.rst b/Doc/library/math.rst
index ff937d2..776768d 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -394,7 +394,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 90b8b7b..6edc3e1 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -826,6 +826,10 @@ class MathTests(unittest.TestCase):
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)
@@ -866,8 +870,6 @@ class MathTests(unittest.TestCase):
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 0000000..048478c
--- /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 966b99b..84561b9 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 dd596c8..4e97337 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2418,14 +2418,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)))
@@ -2433,16 +2433,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) {
@@ -2473,12 +2491,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;
}