[New-bugs-announce] [issue39554] @functools.lru_cache() not respecting typed=False

Benoit B report at bugs.python.org
Tue Feb 4 22:47:25 EST 2020


New submission from Benoit B <hi at benbernardblog.com>:

I don't know if I'm missing something, but there's a behavior of functools.lru_cache() that I currently don't understand.

As the documentation states:

"If typed is set to true, function arguments of different types will be cached separately. For example, f(3) and f(3.0) will be treated as distinct calls with distinct results."

For a function accepting only positional arguments, using typed=False doesn't seem to be working in all cases.

>>> import functools
>>> 
>>> @functools.lru_cache()      # Implicitly uses typed=False
>>> def func(a):
...     return a
>>> 
>>> func(1)
>>> func(1.0)
>>> 
>>> print(func.cache_info())
CacheInfo(hits=0, misses=2, maxsize=128, currsize=2)

Instead, I would have expected: CacheInfo(hits=1, misses=1, maxsize=128, currsize=2)

So it looks like 1 and 1.0 were stored as different values even though typed=False was used.

After analyzing the source code of _functoolsmodule.c::lru_cache_make_key(), I found what follows:

    if (!typed && !kwds_size) {
        if (PyTuple_GET_SIZE(args) == 1) {
            key = PyTuple_GET_ITEM(args, 0);
            if (PyUnicode_CheckExact(key) || PyLong_CheckExact(key)) {      <<< it appears that a 'float' would cause 'args' (a tuple) to be returned as the key, whereas an 'int' would cause 'key'
                /* For common scalar keys, save space by                        (an int) to be returned as the key. So 1 and 1.0 generate different hashes and are stored as different items.
                   dropping the enclosing args tuple  */
                Py_INCREF(key);
                return key;
            }
        }
        Py_INCREF(args);
        return args;
    }

At some point in the past, the above code section looked like this:

    if (!typed && !kwds) {
        Py_INCREF(args);
        return args;
    }
    
So no matter what the type of the argument was, it was working.
    
Am I somehow mistaken in my analysis or is this a bug?

----------
components: Library (Lib)
messages: 361404
nosy: bbernard
priority: normal
severity: normal
status: open
title: @functools.lru_cache() not respecting typed=False
type: behavior
versions: Python 3.8

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue39554>
_______________________________________


More information about the New-bugs-announce mailing list