osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Do not promote `None` as the first argument to `filter` in documentation.


Chris Angelico wrote:

> On Wed, Mar 7, 2018 at 2:12 AM, Kirill Balunov <kirillbalunov at gmail.com>
> wrote:
>>
>>
>> 2018-03-06 17:55 GMT+03:00 Chris Angelico <rosuav at gmail.com>:
>>>
>>> On Wed, Mar 7, 2018 at 1:48 AM, Kirill Balunov <kirillbalunov at gmail.com>
>>> wrote:
>>> > Note: For some historical reasons as the first argument you can use
>>> > None instead of function, in this case the identity function is
>>> > assumed. That is, all elements of iterable that are false are removed
>>> > which is equivalent
>>> > to (item for item in iterable if item). Currently, for the same
>>> > purpose the
>>> > preferred form is `filter(bool, iterable)`.
>>> >
>>>
>>> I'd prefer to word it something like:
>>>
>>> If the first argument is None, the identity function is assumed. That
>>> is, all elements of the iterable that are false are removed; it is
>>> equivalent to (item for item in iterable if item). It is approximately
>>> equivalent to (but faster than) filter(bool, iterable).
>>>
>>> ChrisA
>>> --
>>> https://mail.python.org/mailman/listinfo/python-list
>>
>>
>> I do not want to seem rude and stubborn, but how much faster is it to
>> highlight or emphasize it:
>>
> 
> Timings mean little. Why do we write:
> 
> if lst:
> 
> instead of:
> 
> if bool(lst):
> 
> ? Because it's unnecessary and pointless to call bool() on something
> before using it in a boolean context. If that concept causes you
> problems, it's not the fault of the filter function; filter simply
> uses something in a boolean context.
> 
> So "the identity function" is more correct than "the bool() function".

Fun fact: CPython handles filter(bool) and filter(None) the same way, it 
sets the checktrue flag and chooses the fast path:

static PyObject *
filter_next(filterobject *lz)
{
    PyObject *item;
    PyObject *it = lz->it;
    long ok;
    PyObject *(*iternext)(PyObject *);
    int checktrue = lz->func == Py_None || lz->func == (PyObject 
*)&PyBool_Type;

    iternext = *Py_TYPE(it)->tp_iternext;
    for (;;) {
        item = iternext(it);
        if (item == NULL)
            return NULL;

        if (checktrue) {
            ok = PyObject_IsTrue(item);
        } else {
            PyObject *good;
            good = PyObject_CallFunctionObjArgs(lz->func, item, NULL);
            if (good == NULL) {
                Py_DECREF(item);
                return NULL;
            }
            ok = PyObject_IsTrue(good);
            Py_DECREF(good);
        }
        if (ok > 0)
            return item;
        Py_DECREF(item);
        if (ok < 0)
            return NULL;
    }
}

If there were a built-in identity() function it could be treated the same 
way.