Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate function with arguments filled in when creating it?

Tags:

python

My goal is to generate functions dynamically and then save them in a file. For e.g, in my current attempt, On calling create_file

import io


def create_file(a_value):
    a_func = make_concrete_func(a_value)
    write_to_file([a_func], '/tmp/code.py')


def make_concrete_func(a_value):
    def concrete_func(b, k):
        return b + k + a_value

    return concrete_func


def write_to_file(code_list, path):
    import inspect
    code_str_list = [inspect.getsource(c) for c in code_list]
    with open(path, 'w') as ofh:
        for c in code_str_list:
            fh = io.StringIO(c)
            ofh.writelines(fh.readlines())
            ofh.write('\n')


create_file('my_value')

The output I want is (file /tmp/code.py):

def concrete_func(b, k):
    return b + k + 'my_value'

The output I get is (file '/tmp/code.py'):

def concrete_func(b, k):
    return b + k + a_value

UPDATE: My solution uses inspect.getsource which returns a string. I wonder if I have limited your options as most solutions below suggest a string replacement. The solution need not use inspect.getsource. You could write it anyhow to get the desired output.

UPDATE 2: The reason I am doing this is because I want to generate a file for Amazon Lambda. Amazon Lambda takes a python file and its virtual environment and will execute it for you(relieving you from worrying about scalability and fault tolerance). You have to tell Lambda which file and which function to call and Lambda will execute it for you.

like image 491
RAbraham Avatar asked Jan 26 '17 20:01

RAbraham


1 Answers

A function definition doesn't look up its free variables (variables that are not defined in the function itself) at time of definition. I.e. concrete_func here:

def make_concrete_func(a_value):
    def concrete_func(b, k):
        return b + k + a_value

    return concrete_func

doesn't look up a_value when it is defined, instead it will contain code to load a_value from its closure (simplified the enclosing function) at runtime.

You can see this by disassembling the returned function:

f = make_concrete_func(42)

import dis
print dis.dis(f)

  3           0 LOAD_FAST                0 (b)
              3 LOAD_FAST                1 (k)
              6 BINARY_ADD          
              7 LOAD_DEREF               0 (a_value)
             10 BINARY_ADD          
             11 RETURN_VALUE        
None

You can maybe do what you want by editing the byte code.. it's been done before (http://bytecodehacks.sourceforge.net/bch-docs/bch/module-bytecodehacks.macro.html ..shudder).

like image 52
thebjorn Avatar answered Oct 09 '22 22:10

thebjorn