Consider the following code:
import random
class Trie:
def __init__(self, children, end):
self.children = children
self.end = end
def trie_empty():
return Trie(dict(), False)
def trie_insert(x, t):
if not x:
t.end = True
return
try:
t2 = t.children[x[0]]
except KeyError:
t2 = trie_empty()
t.children[x[0]] = t2
trie_insert(x[1:], t2)
def fill_dict(root):
memo = dict()
def fill(pfx='', depth=0):
try:
memo[pfx]
except KeyError:
pass
else:
return
if depth > 6:
return
for ci in range(ord('a'), ord('d') + 1):
fill(pfx + chr(ci), depth + 1)
bw = None
memo[pfx] = None, bw
fill()
# del memo
def random_word():
l = int(random.random() * 10)
w = ''.join([chr(int(random.random() * 26) + ord('a')) for _ in range(l)])
return w
def main():
t = trie_empty()
for _ in range(10000):
trie_insert(random_word(), t)
while True:
fill_dict(t)
if __name__ == '__main__':
main()
When I run this, it continues to use more memory until I kill it. If I uncomment the del memo
, it runs while using a constant amount of memory. From this, I conclude that the local variable memo
is not being cleaned up when fill_dict
returns.
This behavior is really mysterious to me, especially because basically all of the above code is necessary to see this behavior. even the completely unused argument to fill_dict
cannot be omitted for the program to use unbounded memory.
This is really frustrating. Surely a modern, garbage-collected language can clean up its own variables and I shouldn't have to manually delete function-local variables. Even C can clean up the stack when a function returns. Why can't Python (in this situation)?