Or does it keep separated, in memory, a cache or a table where un-referenced values are stored?
Your guess above is more or less correct in CPython. There is a small integer intern, which holds integers -5 to 256 inclusive (on a default CPython build). The memory for each integer is preallocated, so each assignment statement you've shown here only retrieves the cached object from the intern.
Neither 5 nor 7 will be created nor deleted because they were already interned, and they will remain in the intern until the interpreter exits.
So, assigning to these values will simply increment or decrement their reference counts, as you can check by using stdlib gc module:
>>> import gc
>>> def counts():
... print("refcount5:", len(gc.get_referrers(5)))
... print("refcount7:", len(gc.get_referrers(7)))
...
>>> counts()
refcount5: 10
refcount7: 7
>>> myInt = 5 # this will increment 5's refcount
>>> counts()
refcount5: 11
refcount7: 7
>>> myInt = 7 # this will decrement 5's refcount and increment 7's
>>> counts()
refcount5: 10
refcount7: 8
>>> myInt2 = 5 # this will increment 5's refcount
>>> counts()
refcount5: 11
refcount7: 8
You can see the code which retrieves from intern here:
#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
...
PyObject *
PyLong_FromLong(long ival)
{
...
if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival);
}
...
}
Note that interning integers is an implementation detail.