modify recursive function without copy-paste python - python

I found that I want to modify a recursive function's behavior for a specific input. I know I can do this by rewriting the function, but as the function could be long I want to avoid duplicating code.
As an example, let's say I have implemented the function lambda x: max(x, 10) in the following recursive way:
def orig_fun(x):
if x < 10:
return orig_fun(x + 1)
return x
but now I want to return 20 whenever the input is 5, as in the following
def desired_fun(x):
if x == 5:
return 20
if x < 10:
return orig_fun(x + 1)
return x
which is the same as adding an if statement in the begging of orig_fun or writing a new function copying the body of orig_fun. I don't want to do this because the body must be many many lines. Of course, doing new_fun = lambda x: 20 if x == 5 else orig_fun(x) does not work because new_fun(3) would be 3 instead of 20.
Is there a way I can solve this in Python3?
note that this is a duplicate of Extend recursive (library) function without code duplication which has no satisfying answer (some user talked about "hacky ways" not presented)

You can use a another function to wrap your main function like that:
def orig_fun(x):
if x < 10:
return orig_fun(x + 1)
return x
def wrapper(x):
if x == 5:
return 20
return orig_fun(x)
>>> print(wrapper(8)) # output: 10
>>> print(wrapper(5)) # output: 20
>>> print(wrapper(12)) # output: 12
update
so you want to change (extend) logic in your recursive function without touching it, then let's make your recursive function non-recursive!
# store orig_fun in another location
main_fun = orig_fun
# re-define orig_fun so it will do one more
# step in every call
def orig_fun(x):
if x == 5:
return 20
return main_fun(x)
>>> orig_fun(2) # output: 20
>>> orig_fun(5) # output: 20
>>> orig_fun(7) # output: 10
>>> orig_fun(12) # output: 12
>>> orig_fun(35) # output: 35

Related

Simple Python Issue with Iteration and Lambda

lst = [1, 2, 3]
i = 0
f = lambda x: x * lst[i]
i = 1
print(f(10))
f = lambda x: x * lst[i]
i = 2
print(f(10))
f = lambda x: x * lst[i]
Above is my Python code and I thought it would print out 10, 20, but it says 20, 30. I don't understand why f is modified by i regardless of the explicit assignment. I've got to make it print 10, 20 using an iteration(actually the code is a simplified form of the original one), so it seems that f = lambda x: x * 1 is not allowed.
I think you're expecting f = lambda x: x * lst[i] to store value of i, but it doesn't work that way. When a function is defined, it is just stored to be used later, it is not evaluated when it is defined. It is evaluated only when it is called.
So, when you call f(10) for the first time, you're passing value of x as 10 and the interpreter looks up for the value of i in memory, which is 1 during first function call and 2 during second function call. That's why you get 20 30 as output.
Feel free to ask any question if you still have doubts.
i is global variable. so when you call f it uses the current value of i
look at
f = lambda x: x * lst[i]
lst = [1, 2, 3]
for i in range(len(lst)):
print(f(10))
output
10
20
30
Note, not related to your question, but f = lambda x: x * lst[i] is against PEP8 recommendations:
Always use a def statement instead of an assignment statement that
binds a lambda expression directly to an identifier:
Correct: def f(x): return 2*x
Wrong: f = lambda x: 2*x
The first form means that the name of the resulting function object is
specifically 'f' instead of the generic ''. This is more
useful for tracebacks and string representations in general. The use
of the assignment statement eliminates the sole benefit a lambda
expression can offer over an explicit def statement (i.e. that it can
be embedded inside a larger expression)
Imagine lambda expression as a truly function, so you will get these code:
lst = [1, 2, 3]
i = 0
def f(x):
return x * lst[i]
i = 1
print(f(10)) # currently x=10 and i=1
def f(x):
return x * lst[i]
i = 2
print(f(10)) # currently x=10 and i=2
def f(x):
return x * lst[i]
I know what you mean. In your head, while you define lambda x: x * lst[i], you think number i will be frozen at this time --- just like assignment. When it be called later, the number i will always be the first value. It's not True. The value of i will be referenced when you call this function.

A question regarding the Lambda function in Python

Why in the following code, the output is 22?
In my understanding, we have a function that needs two arguments, but it has been defined with only one! However, the first time we use it in mydoubler = myfunc(2), it assigns the argument(2) to variable n, but the second time we use it in print(mydoubler(11), it uses the argument(11) to set the value of the variable a! Why is that? Does Lambda work like a recursive function?
def myfunc(n):
return lambda a : a * n
mydoubler = myfunc(2)
print(mydoubler(11))
Basically what happens is this:
mydoubler = myfunc(2) is actually the same as writing mydoubler = lambda a : a * 2
The reason for this is that myfunc(2) returns lambda a : a * 2
So now mydoubler = lambda a : a * 2
Then when mydoubler(11) is called, it simply returns 11 * 2
You're returning a lambda, which is a one-liner function, NOT a number. The code below does the EXACT SAME thing, but is maybe a bit clearer as to its purpose:
def multiplier_factory(constant_factor):
# Define our new function
def multiplier(factor):
result = constant_factor * factor
return result
# Return the FUNCTION, not a number
return multiplier
doubler = multiplier_factory(2)
tripler = multiplier_factory(3)
print (doubler(1)) # prints 2
print (tripler(1)) # prints 3
print (doubler('a')) # prints 'aa'
print (tripler('a')) # prints 'aaa'
lambda a: a * n is the same of:
def somefunction(a):
return a * n
When you called myfunc(2) you dynamically created a function that is the same of:
def somefunction(a):
return a * 2
myfunc returns a function. So mydoubler is a function and is described by lamda a : a * 2. Then you call that function with the argument 22 and so naturally 11 * 2 = 22 is printed. Lambda functions are not per se recursive, they are just a shorter way of writing a simple function. In your case you can also write:
def myfunc(n):
def multiplier(a):
return a * n
return multiplier

How to change a variable inside a function scope after defining the function in Python?

I'm looking for a way to change the variables defined inside a function after defining the function.
For example
def GetNthPower(x) :
n = None
return x**n
my_numbers_list = [11,23,45,56,78,98]
# now if I feel like I need the 4th power of some numbers in the list
GetNthPower.n = 4
for x in my_numbers_list :
print GetNthPower(x)
# If I want 7th power then
GetNthPower.n = 7
This obviously will not work, is there any way to do this?
N.B: I know we can achieve this by setting 'n' as an argument of the function, but I want to do it this way for a particular reason.
I want my function to have only one argument (for using the function in multiprocessing.Pool.map()).
You can define static variables inside functions, almost like you did:
def GetNthPower(x) :
return x ** GetNthPower.n
GetNthPower.n = 3
print(GetNthPower(2)) #8
Make sure to initialize correctly your GetNthPower.n before first use though.
If you're worried about initialization, you could go for this version which uses a default value 1:
def GetNthPower(x) :
return x ** (GetNthPower.n if hasattr(GetNthPower, "n") else 1)
I think it would still be better for you to write a function that takes two arguments, or use the predefined ** operator.
Don't use one function; create a function that makes your function, using a closure.
def nth_power_maker(n):
def _(x):
return x ** n
return _
my_numbers_list = [11,23,45,56,78,98]
# now if I feel like I need the 4th power of some numbers in the list
get_4th_power = nth_power_maker(4)
for x in my_numbers_list:
print(get_4th_power(x))
get_7th_power = nth_power_maker(7)
Alternatively, you could use functools.partial to bind a keyword argument to a function
from functools import partial
def get_nth_power(x, n):
return x ** n
get_third = partial(get_nth_power, n=3)
get_third(4)
64
x = 4
# in a loop
for pow in [2, 4, 6, 8]:
f = partial(get_nth_power, n=pow)
f(x)

Factory functions advice/explanation

I have started this question regarding bear options however I am unsure how to proceed with the factory function part:
a) A bear option has payoff
see image
I have to write a python function which returns the value of the payoff.
def bear(S,K):
if S <= K:
value = K
elif K < S and S < 2*K:
value = 2*K - S
else:
value = 0
return value
The next part... b) "Also write a factory function which returns a bear option payoff function of one variable, with K fixed."
I cannot find a simple explanation of what a factory function is, I am just starting to code and my notes have no mention of factory functions as of yet. Any links to web pages, hints or explanations will be much appreciated!
What they want you to do is write a function that returns another function, which can be used to compute bear(S, K) for a fixed value of K.
def bear_for_k(K):
return lambda S: bear(S, K)
Demo:
>>> bear(21, 17)
13
>>> bear_for_k17 = bear_for_k(17)
>>> bear_for_k17(21)
13
>>> bear(112, 81)
50
>>> bear_for_k81 = bear_for_k(81)
>>> bear_for_k81(112)
50
edit in response to comment:
Try the following file:
def bear(S,K):
if S <= K:
value = K
elif K < S and S < 2*K:
value = 2*K - S
else:
value = 0
return value
def bear_for_k(K):
return lambda S: bear(S, K)
#test:
print(bear_for_k(17)(21))
This will print 13, without errors, in Python2 and Python3.

Returning intermediate results from function in Python

Imagine I've got a Python module with some function in it:
def sumvars(x, y, z):
s = x
s += y
s += z
return s
But sometimes I want to get results of some intermediate calculations (for example, I could have a function which reverses a matrix and would like to know the determinant which has been calculated as an intermediate step as well). Obviously, I wouldn't want to redo those calculations again if they were already done within that function.
My first idea is to return a dict:
def sumvars(x, y, z):
d = {}
s = x
d['first_step'] = s
s += y
d['second_step'] = s
s += z
d['final'] = s
return d
But I don't recall any functions in numpy or scipy which return dicts and so it seems like this might be not a good idea. (Why?) Also routinely I'll always have to type sumvars(x,y,z)['final'] for a default return value...
Another option I see is creating global variables but seems wrong having a bunch of them in my module, I would need to remember their names and in addition not being attached to the function itself looks like a bad design choice.
What would be the proper function design for such situation?
Generally when you have two different ways you want to return data, go ahead and make two different functions. "Flat is better than nested", after all. Just have one call the other so that you Don't Repeat Yourself.
For example, in the standard library, urllib.parse has parse_qs (which returns a dict) and parse_qsl (which returns a list). parse_qs just then calls the other:
def parse_qs(...):
parsed_result = {}
pairs = parse_qsl(qs, keep_blank_values, strict_parsing,
encoding=encoding, errors=errors)
for name, value in pairs:
if name in parsed_result:
parsed_result[name].append(value)
else:
parsed_result[name] = [value]
return parsed_result
Pretty straightforward. So in your example it seems fine to have
def sumvars(x, y, z):
return sumvars_with_intermediates(x, y, z).final
def sumvars_with_intermediates(x, y, z):
...
return my_namedtuple(final, first_step, second_step)
(I favor returning namedtuples instead of dicts from my APIs, it's just prettier)
Another obvious example is in re: re.findall is its own function, not some configuration flag to search.
Now, the standard library is a sprawling thing made by many authors, so you'll find counterexamples to every example. You'll far more often see the above pattern rather than one omnibus function that accepts some configuration flags, though, and I find it far more readable.
Put the common calculation into its own function as Jayanth Koushik recommended if that calculation can be named appropriately. If you want to return many values (an intermediate result and a final result) from a single function then a dict may be an overkill depending on what is your goal but in python it is much more natural to simply return a tuple if your function has many values to return:
def myfunc():
intermediate = 5
result = 6
return intermediate, result
# using the function:
intermediate, result = myfunc()
Not sure if function attributes is a good idea:
In [569]: def sumvars(x, y, z):
...: s = x
...: sumvars.first_step = s
...: s += y
...: sumvars.second_step = s
...: s += z
...: return s
In [570]: res=sumvars(1,2,3)
...: print res, sumvars.first_step, sumvars.second_step
...:
6 1 3
Note: as #BrenBarn mentioned, this idea is just like global variables, your previously calculated "intermediate results" could not be stored when you want to reuse them.
Just came up with this idea which could be a better solution:
def sumvars(x, y, z, mode = 'default'):
d = {}
s = x
d['first_step'] = s
s += y
d['second_step'] = s
s += z
d['final'] = s
if mode == 'default':
return s
else:
return d
I belive the proper solution is to use a class, to have a better grasp of what you are modeling. For example in the case of the Matrix, you could simply store the determinant in the "determinant" attribute.
Here is an example using your matrix example.
class Matrix:
determinant = 0
def calculate_determinant(self):
#calculations
return determinant
def some_method(self, args):
# some calculations here
self.determinant = self.calculate_determinant()
# other calculations
matrix = Matrix()
matrix.some_method(x, y, z)
print matrix.determinant
This also allows you to separate your method into simpler methods, like one for calculating the determinant of your matrix.
Another variation:
def sumvars(x, y, z, d=None):
s = x
if not d is None:
d['first_step'] = s
s += y
if not d is None:
d['second_step'] = s
s += z
return s
The function always returns the desired value without packing it into a tuple or dictionary. The intermediate results are still available, but only if requested. The call
sumvars(1, 2, 3)
just returns 6 without storing intermediate values. But the call
d = {}
sumvars(1, 2, 3, d)
returns the same answer 6 and inserts the intermediate calculations into the supplied dictionary.
Option 1. Make two separate functions.
Option 2. Use a generator:
>>> def my_func():
... yield 1
... yield 2
...
>>> result_gen = my_func()
>>> result_gen
<generator object my_func at 0x7f62a8449370>
>>> next(result_gen)
1
>>> next(result_gen)
2
>>> next(result_gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Inspired by #zhangxaochen solution, here's my take on your problem using class attributes:
class MyClass():
def __init__(self):
self.i = 4
def f(self):
s = self.i
MyClass.first_step = s
print(MyClass.first_step)
s += self.i
MyClass.second_step = s
print(MyClass.second_step)
s += self.i
return s
def main():
x = MyClass()
print(x.f()) # print final s
print(x.first_step)
print(x.second_step)
print(MyClass.second_step)
Note: I included several prints to make it more explicit how attribute values can be retrieved.
Result:
4
8
12
4
8
8

Categories

Resources