This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: PyNumber_Float counterpart that doesn't accept strings
Type: enhancement Stage: resolved
Components: Interpreter Core Versions: Python 3.8
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: mark.dickinson, serhiy.storchaka
Priority: normal Keywords:

Created on 2018-02-08 09:07 by mark.dickinson, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (4)
msg311819 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-08 09:07
Our abstract objects layer in the C-API includes PyNumber_Float [1], which is equivalent to a Python-level `float` call, including the behaviour of accepting strings. I'm not convinced that it's a particularly useful function: I suspect that it's more common to need to either convert a string to a float, or to convert a float-y object (i.e., something whose type implements __float__) to a float, but not both at once. The second need is precisely the one that most of the math module has: accept anything that implements __float__, but don't accept strings.

Yesterday I found myself writing the following code for a 3rd-party extension module:


static PyObject *
_validate_float(PyObject *value) {
    double value_as_double;

    /* Fast path: avoid creating a new object if it's not necessary. */
    if (PyFloat_CheckExact(value)) {
        Py_INCREF(value);
        return value;
    }

    value_as_double = PyFloat_AsDouble(value);
    if (value_as_double == -1.0 && PyErr_Occurred()) {
        return NULL;
    }
    return PyFloat_FromDouble(value_as_double);
}


Would it be worth adding a new C-API level function that does essentially the above? The semantics of such a function seem clear cut. The major problem would be figuring out what to call it, since to me PyNumber_Float is the one obvious name for such behaviour, but it's already taken. :-)

Please note that I'm not suggesting removing / deprecating / changing the existing PyNumber_Float. That would amount to gratuitous breakage.


[1] https://docs.python.org/3.6/c-api/number.html#c.PyNumber_Float
msg311823 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-02-08 09:31
Maybe use PyNumber_Check() to distinguish these two cases?

    if (PyNumber_Check(obj))
        return PyNumber_Float(obj);
    PyErr_SetString(PyExc_TypeError, "not a number");
    return NULL;
msg311824 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-02-08 09:50
For converting a string to a float there is PyFloat_FromString(). So that this part of functionality of PyNumber_Float() is redundant. In long perspective it would be worth to deprecate it.
msg311845 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2018-02-08 19:25
> Maybe use PyNumber_Check() [...]

Ah, that works. It reads better than the `PyFloat_AsDouble` solution, too, since it's far from obvious that `PyFloat_AsDouble` goes to the trouble to coerce a float-y thing to a float.

I did some searching around to see if I could find evidence that anyone besides me thinks this is a good idea, but I didn't come up with much. In the NumPy source, for example, it's more common to want a C `double` than another Python object, and I guess that's going to be true for other 3rd party libraries, too.

And I'm also realising that part of what I need here is a *Python*-level solution to the problem, something like this:

def _validate_float(value):
    """
    Coerce an arbitrary Python object to a float, or raise TypeError.
    """
    if type(value) is float:  # fast path for common case
        return value
    try:
        nb_float = type(value).__float__
    except AttributeError:
        raise TypeError(
            "Object of type {!r} not coerceable to float".format(type(value)))
    return nb_float(value)

(and the _validate_float I posted earlier was really just there because the C extension module needed to be able to do the same thing as the equivalent Python code above).

All in all, I don't think I can make a good case for this. I'll close the issue.
History
Date User Action Args
2022-04-11 14:58:57adminsetgithub: 76975
2018-02-08 19:25:33mark.dickinsonsetstatus: open -> closed
resolution: rejected
messages: + msg311845

stage: resolved
2018-02-08 09:50:40serhiy.storchakasetmessages: + msg311824
2018-02-08 09:31:44serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg311823
2018-02-08 09:07:52mark.dickinsoncreate