0

With the following function:

def func(var):
    print (var)

x = func

Is it possible to modify the code of a function given x? Pretending the "compiled code" is a string. I mean something like this:

>>> x = func
>>> x_as_string = "print (var)"
>>> x_as_string_modified = x_as_string += "\n print('end of function')"
>>> x(2)
2
end of function
martineau
  • 119,623
  • 25
  • 170
  • 301
user12282936
  • 167
  • 2
  • 7
  • 4
    Not in anyway that would be useful. There's probably some way to patch a function's byte code directly, but that would be specific to CPython. – chepner Oct 28 '19 at 17:42
  • It should be possible, will be quite involved. I suggest taking a look at the `inspect` module. – BenedictWilkins Oct 28 '19 at 17:43
  • 5
    I suspect this is an [XY problem](https://meta.stackexchange.com/a/66378/150985). _Why_ do you want to do this? – martineau Oct 28 '19 at 17:45
  • 1
    @martineau this is definitely an XY problem unless this user is curious about the intricacies of the language – Sam Creamer Oct 28 '19 at 17:47

4 Answers4

1

The method you express is a very, very bad idea. Self-modifying code stains your karma, moves you farther from satori and nirvana, and lacks Buddha-nature.

What you can do decently is to write a function that parameterises your variations in a controlled way. Then write a wrapper function to return custom varieties of the function per your choices.

def make_x(str):

    def inner(var):
        print(var)
        if str:
            print(str)

    return inner

orig = make_x(None)
new  = make_x("end of function")

orig(2)
new(77)

Output:

2
77
end of function

is that enough power for your needs?

Prune
  • 76,765
  • 14
  • 60
  • 81
1

I can't stress this enough: Don't ever use this on anything even mildly serious. Exec is dangerous and not a good idea.

But curiosity got the better of me so here's one way to literally add lines to code that is to be executed:

>>> import inspect

    def func(var):
        print (var)

    def terriblie_idea(arg, func = func, new_func_lines = []):
        func_string, call_string =  inspect.getsource(func), f"func({arg})"
        func_string += ('\n' + '\n'.join(new_func_lines)) if new_func_lines else ''

        exec(func_string + ';\n' +call_string)

>>> terriblie_idea(124, new_func_lines=["print(f'This code is dynamic! Your input was {arg}')", "print('end of function')"])

output:

>>> This code is dynamic! Your input was 124
    end of function
    124

This is similar to your idea in the fact that you can input new lines into your original function by passing them into the terrible_idea function as arguments. The function gets rebuilt and then called on the first argument of terrible_idea, which I should mention is a terrible idea.

Brian
  • 1,572
  • 9
  • 18
0

You can use a combination of inspect.getsource and eval.

import inspect
import os.path

os_path_join_code = inspect.getsource(os.path.join)

and for eval:

sum = eval("1 + 2")

Now you put everything together and keep on exploring it. But this is definitely not the best approach for modifying code, you can always modify code behaviour by augmenting it, and it is very possible to do through pre-stablished code patterns. I would start by understanding python function decorator.

H_DANILO
  • 321
  • 1
  • 9
0

I recommend that you look into what are called python decorators.

Consider the following code:

import functools

def decorator(f):
    @functools.wraps(f)
    def wrapped_f(*args, **kwargs):
        print('start of function')
        r = f(*args, **kwargs)
        print('end of function')
        return r
    return wrapped_f

@decorator
def print_strawberries():
    print("strawberries")

print_strawberries()

Even if you don't quite understand the code, know that the text printed to the console is:

start of function
strawberries
end of function

In some way, we have changed print_strawberries. The new version also prints start of function and end of function.

The notation @decorator is equivalent to the following:

def print_strawberries():
    print("strawberries")
print_strawberries = decorator(print_strawberries)

Even better that function decorators are classes which define their own __call__ methods. __call__ is the paren-paren operator (). That is print("hello world") is actually print.__call__("hello world")

class Decorator:
    def __init__(self, callable):
        self._callable = callable
    def __call__(self, *args, **kwargs):
        print('start of function')
        r = self._callable(*args, **kwargs)
        print('end of function')
        return r

###################################################

@Decorator
def print_strawberries():
    print("strawberries")

print_strawberries()

###################################################

def add_two(x, y):
    return x + y
add_two = Decorator(add_two)

result = add_two(0, 1)
print(result)
###################################################
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42