How to maintain state in Python without classes?

You can define closure in Python in the same way you define a closure in JavaScript.

def get_matcher():
    compiled_regex = compile_my_regex()

    def try_match(m)
        return compiled_regex.match(m)

    return try_match

However, in Python 2.x closures are read-only (you cannot re-assign to compiled_regex inside function call, for the example above). If the closure variable is a mutable data structure (e.g. list, dict, set), you can modify it inside your function call though.

def get_matcher():
    compiled_regex = compile_my_regex()
    match_cache = {}

    def try_match(m):
        if m not in match_cache:
           match_cache[m] = compiled_regex.match(m)

        return match_cache[m]

    return try_match

In Python 3.x , you can use the nonlocal keyword to re-assign to closure variable in function call. (PEP-3104)

Also see the following questions on closure in Python:

  • What limitations have closures in Python compared to language X closures?
  • Read/Write Python Closures

You can also accomplish this with default arguments:

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match):
    return re_match(m)

since default arguments are only evaluated once, at module import time.

Or even simpler:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m)

Or simplest yet:

try_match = re.compile(r'sldkjlsdjf').match

This saves not only the re compile time (which is actually cached internally in the re module anyway), but also the lookup of the '.match' method. In a busy function or a tight loop, those '.' resolutions can add up.


What about

def create_matcher(re):
    compiled_regex = compile_my_regex()
    def try_match(m):
        return compiled_regex.match(m)
    return try_match

matcher = create_matcher(r'(.*)-(.*)')
print matcher("1-2")

?

But classes are better and cleaner in most cases.


You can stash an attribute in any function. Since the function name is global, you can retrieve it in other functions. For example:

def memorize(t):
    memorize.value = t

def get():
    return memorize.value

memorize(5)
print get()

Output:

5

You can use it to store state in a single function:

def memory(t = None):
    if t:
        memory.value = t
    return memory.value

print memory(5)
print memory()
print memory()
print memory(7)
print memory()
print memory()

Output:

5
5
5
7
7
7

Granted its usefulness is limited. I've only used it on SO in this question.