Passing command line arguments to a function that calls a decorated function with decorator arguments

464 views python
2

This example is contrived but represents a real life situation:

  1. I have a python script that takes command line arguments.

  2. main() will parse the arguments, and pass them on to an intermediate function (caller_func in the code example)

  3. The intermediate function will then call a decorated function (fib() in the example) that is decorated with lru_cache from functools, and the maxsize of the cache is an argument to be accepted from command line and passed via the intermediate function.

How do I do this?

import argparse
from functools import lru_cache

def main():
    # boilerplate for parsing command line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("--cache_size", default="10")
    parser.add_argument("--fibo_num", default="20")
    args = parser.parse_args()
    cache_size = int(args.cache_size)
    fibo_num = int(args.fibo_num)

    caller_func(cache_size, fibo_num)

#Intermediate function that is supposed to call decorated function
def caller_func(cache_size, fib_num): 
    print(fib(fib_num))

#function decorated by LRU cache
@lru_cache(maxsize=cache_size)
def fib(n): 
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

if __name__ == "__main__":
    main()

run as

python3 example.py --cache_size 5 --fibo_num 30

throws

NameError: name 'cache_size' is not defined

I tried making cache_size a global variable, but it didn't work, and I don't want globals anyway.

answered question

The decorator is executed at definition time, before the call to main is even reached.

Perhaps use partial func as here: stackoverflow.com/a/25827070/3279716

Nice diagnosis @jonrsharpe. What's the solution then? Is it possible to decorate this decorator from command line arguments?

You don't have to apply the decorator like that; given that the intermediate takes cache size anyway (what for up until now?) you can call lru_cache(maxsize=cache_size)(fib)(fibo_num).

1 Answer

11

You don't have to use a decorator with decorator syntax. You can wait to "decorate" fib until after you have the desired cache size. For example,

import argparse
from functools import lru_cache

def main():
    global fib
    # boilerplate for parsing command line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("--cache_size", default="10")
    parser.add_argument("--fibo_num", default="20")
    args = parser.parse_args()
    cache_size = int(args.cache_size)
    fibo_num = int(args.fibo_num)

    fib = lru_cache(maxsize=cache_size)(fib)

    caller_func(fibo_num)

#Intermediate function that is supposed to call decorated function
def caller_func(fib_num): 
    print(fib(fib_num))

def fib(n): 
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

if __name__ == "__main__":
    main()

posted this

Have an answer?

JD

Please login first before posting an answer.