Due to complexity of virtual memory management (all of which is for the benefit of using the least amount possible), it's practically impossible to determine how much RAM is actually in use. See this link.
So whatever your python script reports, it won't reflect the actual state.
What's cached is actually free, no worries about that (dropping caches doesn't actually free anything, it just flushes the pages kernel would reuse if it needed them).
What free reports is what the kernel knows about the system. It can't be wrong, as the kernel is the one that's serving the memory. But it doesn't equal the sum of used memory of each individual process, because of various mechanisms: shared memory (libraries), copy-on-write memory (after forking, only pages that are touched are actually duplicated), uninitialized (zeroed out) pages, loaded program code (also shared), virtual memory not corresponding to RAM, swapped out pages, interprocess shared memory, kernel memory (reserved by kernel modules), kernel memory (the main kernel itself, including the page table), and so on...
The point is... if kernel reports pages as used, they must be used for something. If you want to free up some memory, it must come from somewhere: each running process, module and the kernel itself, may have mechanisms to release some memory they may not need now and will reload later (it's up to application writers if they saw the need to implement needed complexity to do that). But the kernel will take care of it if you really need new memory. It'll drop filesystem caches if you request more memory, it will push stale pages to swap if you are using it, if you use zram or something like that, it'll compress pages instead... and at the end, if you really run out of space, OOM killer will find kill something to prevent the system from locking up. But the process is way too complicated for you to think you know better than whatever is going on inside.