Python recursive function - refer to current and previous arguments - python

I pass new arguments (previous_high, previous_score) to announce_highest, so how do I refer the the old arguments in order to do a comparison?
Sorry if this question is quite basic, still learning how recursive functions work!
def announce_highest(who, previous_high=0, previous_score=0):
"""Return a commentary function that announces when WHO's score
increases by more than ever before in the game.
>>> f0 = announce_highest(1) # Only announce Player 1 score gains
>>> f1 = f0(11, 0)
>>> f2 = f1(11, 1)
1 point! That's the biggest gain yet for Player 1
>>> f3 = f2(20, 1)
>>> f4 = f3(5, 20) # Player 1 gets 4 points, then Swine Swap applies
19 points! That's the biggest gain yet for Player 1
>>> f5 = f4(20, 40) # Player 0 gets 35 points, then Swine Swap applies
20 points! That's the biggest gain yet for Player 1
>>> f6 = f5(20, 55) # Player 1 gets 15 points; not enough for a new high
"""
assert who == 0 or who == 1, 'The who argument should indicate a player.'
# BEGIN PROBLEM 7
gain = #compare previous argument to current argument
if gain > previous_high:
if gain == 1:
print(gain, 'point! That\'s the biggest gain yet for Player', who)
else:
print(gain, 'points! That\'s the biggest gain yet for Player', who)
return announce_highest(some args)
# END PROBLEM 7

The problem from CS61A is described here.
Your first mistake is trying to make this a recursive function. They are asking you to write a higher-order function (a higher-order function is a function that either takes a function as arguments or returns a function as part of its output; in our case we want one that returns a function).
Specifically, we want announce_highest to return a closure. A closure is basically a function (something that takes input, processes it, and returns output) that additionally has its own local environment of defined internal variables that it inherited from the environment where the closure was defined.
Your closure should have three internal variables:
who, the player to announce when their score increases (either 0 or 1)
previous_high the highest score that player you are tracking has ever had
previous_score the score that the player you tracking last had
and this closure takes two parameters current_player0_score and current_player1_score.
Without doing your homework for you, here's two examples of a similar higher-order function that returns a closure. First, I create a my_counter_factory closure creator. It's a function that when called creates a counter which announces when the count is even or odd (depending on the value of the variables announce_if_even_count and announce_if_odd_count variables when it was initially created).
def my_counter_factory(initial_count=0, announce_if_even_count=True, announce_if_odd_count=False):
count = initial_count
def counter(increment_by):
new_count = count + increment_by
if announce_if_even_count and new_count % 2 == 0:
print("Count is even %s" % new_count)
if announce_if_odd_count and new_count % 2 == 1:
print("Count is odd %s" % new_count)
return my_counter_factory(new_count, announce_if_even_count, announce_if_odd_count)
return counter
Which when run will work as:
>>> c0 = my_counter_factory(10, True, True) # count = 10, announce count if even or odd
>>> c1 = c0(5) # announces 15 creates new counter `c1` with count of 15
# note count of c0 is still 10.
Count is odd 15
>>> c2 = c1(3) # count is 18 (stored in new closure c2) by adding 3 to c1 count.
Count is even 18
# See value of closures by incrementing 0 to them:
>>> ignore = c0(0)
Count is even 10
>>> ignore = c1(0)
Count is odd 15
>>> ignore = c2(0)
Count is even 18
Note every time this closure is called, it returns a brand new closure (with its own new environment). An equally valid choice (that behaves slightly differently) is to not create new closures each time, but keep returning back the same closure.
def my_counter_factory(initial_count=0, announce_if_even_count=True, announce_if_odd_count=False):
count = initial_count
def counter(increment_by):
nonlocal count # more on this below.
count += increment_by
if announce_if_even_count and count % 2 == 0:
print("Count is even %s" % count)
if announce_if_odd_count and count % 2 == 1:
print("Count is odd %s" % count)
return counter # This returns back same closure each time.
return counter
This will work as:
>>> c0 = my_counter_factory(10, True, True) # count = 10, announce count if even or odd
>>> c1 = c0(5) # adds 5 to count of c0 closure (was 10, now 15) announces 15
# c1 is a reference to the same closure as c0 with the same count
Count is odd 15
>>> c2 = c1(3) # adds 3 to count of c1 closure (now at 18),
Count is even 18
# See value of closures by incrementing 0 to them:
>>> ignore = c0(0)
Count is even 18
>>> ignore = c1(0)
Count is even 18
>>> ignore = c2(0)
Count is even 18
>>> c0 == c1 == c2
True
# Note in this second example all three closures are identical,
# because the closure merely returns a reference to itself when called
# Granted you could create a brand new closure
>>> new_c = my_counter_factory(0, True, False)
# This one starts with a count of 0 and only announces when it's even
>>> new_c2 = new_c(5)
Final Note: You may wonder why in the second example, we needed the line nonlocal count (but we didn't need it in the first example) and if you took it out you would get an error saying local variable 'count' referenced before assignment. Closures in python are allowed to reference variables from the environment it was defined in (without using the nonlocal keyword introduced in python3), as long as they don't re-assign values to them. Basically when python tries to interpret your function it differentiates between local variables (defined within the function) and variables defined elsewhere. For example:
>>> def print_0_to_4():
... for i in range(5):
... print(i, end=", ")
... print("")
...
>>> i=-1
>>> print_0_to_4()
0, 1, 2, 3, 4,
>>> i
-1
The important thing to note is we called a function print_0_to_4 which assigned to a variable i, which made i a local variable. Changes to the local variable in the function don't modify the values of variables with the same name from the outer environment. (Otherwise programming would be ridiculously hard because we need to know the names of internal variables of every function we'd call for fear of unintentionally modifying our variables when calling a function).
Also note if you don't assign/modify to a variable in a function, then it is ok to reference a variable defined in another scope (without needing a nonlocal keyword).
>>> i = -1
>>> def print_i_from_outer_scope():
... print(i)
...
>>> print_i_from_outer_scope()
-1

Related

How to assign value to function parameter in python

I'd like to initialise a variable inside a function (so that the final print statement in my example outputs 10):
def init_param(param):
param = 10
n = 1
init_param(n)
print n # prints 1
Is this doable in python?
Arguments are assigned inside the function as it's local variables. So all principles apply here.
Immutable objects cannot be changed.
Mutable objects can be modified in place.
you're indenting to modify an immutable object, which is not possible. So your only options are :-
def init_param(param):
param = 10
return param
n = 1
n = init_param(n)
print n
which is pretty much useless OR
def init_param(param):
param[0] = 10
n = [1]
init_param(n)
print n
First of all python function passes the value by object and the reference name here param is just a reference to a value hold by n.
Now coming to the solution, yes it could be possible provided you pass the variable name
def init_param(var_name):
globals()[var_name] = 10
n = 1
init_param('n')
print n
Hope it will answer!
Short answer: no, you can't.
Longer answer: in
def init_param(param):
param = 10
the name param is local to the init_param function. Rebinding this name will change the value bound to the name param in the function's local scope, but will have absolutely no effect on the name n in the caller's scope - those names live in totally distinct namespaces. You can read Ned Batcheler's reference article on Python's names and binding for more in-depth explanations.
What would work would be to use a mutable container - a dict for example - and mutate this container, ie:
def init_param(params, name, value):
params[name] = value
params = {
"n": 1,
"answer": 42,
"parrot": "dead"
}
init_params(params, "n", 10)
print(params)
(if you don't understand why this one works, re-read Ned Batcheler's article linked above)
def init_param(param):
param = 10
n = 1
init_param(n)
print n
here n is a integer (immutable data type) so it will be passed by value and so value of n will be unchanged.
lets take a mutable data type (ex. list) then it will be passed by referenced and so values in list will be changed.
def init_param(a):
a[0] = 10
arr = [1]
init_param(arr)
print(arr[0]) # print 10
so you have to check first whether the data is mutable or immutable.
otherwise you can use global keyword to access global variables.
def f():
global n
n = 10
n = 1
f()
print(n) # print 10

How to implement a static counter in python

I am calling a method and I need a static counter within this method. It's required to parse the elements of the list. The counter will tell which position of the list to lookup at.
For e.g
static_var_with_position = 0
noib_list = [3, 2, 2, 2, 2, 1, 2, 2]
def foo(orig_output, NOB):
# tried two ways
#static_var_with_position += 1 # doesn't work
#global static_var_with_position
#static_var_with_position += 1 # doesn't work either
bit_required = noib_list[static_var_with_position]
converted_output = convert_output(orig_output, NOB, bit_required)
The static_var_with_position value is never incremented. I have commented the two ways I tried to increment value.
In c++ its piece of cake, but I couldn't find anything similar in python so far. Any help will be appreciated :)
Thanks!
Instead of using a global/static counter variable, you could use an iterator:
iterator = iter(noib_list)
def foo(orig_output, NOB):
bit_required = next(iterator)
converted_output = convert_output(orig_output, NOB, bit_required)
The iterator will automatically keep track which is the next element internally.
When the iterator is exhausted (i.e. when you reached the end of the list), next will raise a StopIteration error, so it you do not know when the end is reached, you can use bit_required = next(iterator, None) instead; then just test whether the value is None and you know that the list is exhausted (or just use try/except).
Following this example, you could do the same with your counter :
def inc() :
global global_cpt
global_cpt +=1
print global_cpt
if __name__ == '__main__' :
global_cpt = 0
inc()
inc()
inc()
will print
> 1
> 2
> 3
I don't actually advocate doing this in your case, but it's a little-known hack for creating a "static" variable within a function: put it as a parameter with a mutable default value! You can modify it within the function and it will hold until the next function call, as long as the caller doesn't pass a value for it.
def foo(value=[0]):
value[0] += 1
print(value[0])
>>> foo()
1
>>> foo()
2
>>> foo()
3
>>> foo([906])
907
>>> foo()
4

Odd threading behavior in python

I have a problem where I need to pass the index of an array to a function which I define inline. The function then gets passed as a parameter to another function which will eventually call it as a callback.
The thing is, when the code gets called, the value of the index is all wrong. I eventually solved this by creating an ugly workaround but I am interested in understanding what is happening here. I created a minimal example to demonstrate the problem:
from __future__ import print_function
import threading
def works_as_expected():
for i in range(10):
run_in_thread(lambda: print('the number is: {}'.format(i)))
def not_as_expected():
for i in range(10):
run_later_in_thread(lambda: print('the number is: {}'.format(i)))
def run_in_thread(f):
threading.Thread(target=f).start()
threads_to_run_later = []
def run_later_in_thread(f):
threads_to_run_later.append(threading.Thread(target=f))
print('this works as expected:\n')
works_as_expected()
print('\nthis does not work as expected:\n')
not_as_expected()
for t in threads_to_run_later: t.start()
Here is the output:
this works as expected:
the number is: 0
the number is: 1
the number is: 2
the number is: 3
the number is: 4
the number is: 6
the number is: 7
the number is: 7
the number is: 8
the number is: 9
this does not work as expected:
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
the number is: 9
Can someone explain what is happening here? I assume it has to do with enclosing scope or something, but an answer with a reference that explains this dark (to me) corner of python scoping would be valuable to me.
I'm running this on python 2.7.11
This is a result of how closures and scopes work in python.
What is happening is that i is bound within the scope of the not_as_expected function. So even though you're feeding a lambda function to the thread, the variable it's using is being shared between each lambda and each thread.
Consider this example:
def make_function():
i = 1
def inside_function():
print i
i = 2
return inside_function
f = make_function()
f()
What number do you think it will print? The i = 1 before the function was defined or the i = 2 after?
It's going to print the current value of i (i.e. 2). It doesn't matter what the value of i was when the function was made, it's always going to use the current value. The same thing is happening with your lambda functions.
Even in your expected results you can see it didn't always work right, it skipped 5 and displayed 7 twice. What is happening in that case is that each lambda is usually running before the loop gets to the next iteration. But in some cases (like the 5) the loop manages to get through two iterations before control is passed to one of the other threads, and i increments twice and a number is skipped. In other cases (like the 7) two threads manage to run while the loop is still in the same iteration and since i doesn't change between the two threads, the same value gets printed.
If you instead did this:
def function_maker(i):
return lambda: print('the number is: {}'.format(i))
def not_as_expected():
for i in range(10):
run_later_in_thread(function_maker(i))
The i variable gets bound inside function_maker along with the lambda function. Each lambda function will be referencing a different variable, and it will work as expected.
A closure in Python captures the free variables, not their current values at the time of the creation of the closure. For example:
def capture_test():
i = 1
def foo():
return i
def bar():
return i
print(foo(), bar()) # 1 1
i = 2
print(foo(), bar()) # 2 2
In Python you can also capture variables and write to them:
def incdec():
counter = 0
def inc(x):
nonlocal counter
counter += x
return counter
def dec(x):
nonlocal counter
counter -= x
return counter
return inc, dec
i1, d1 = incdec()
i2, d2 = incdec()
print(i1(10), i1(20), d1(3)) # 10 30 27
print(i2(100), d2(5), d2(20)) # 100 95 75
print(i1(7), d2(9)) # 34 66
As you see incdec returns a pair of two closures that captured the same variable and that are incrementing/decrementing it. The variable shared by i1/d1 is however different from the variable shared by i2/d2.
One common mistake is for example to expect that
L = []
for i in range(10):
L.append(lambda : i)
for x in L:
print(x())
will display the numbers from 0 to 9... all of the unnamed closures here captured the same variable i used to loop and all of them will return the same value when called.
The common Python idiom to solve this problem is
L.append(lambda i=i: i)
i.e. using the fact that default values for parameters are evaluated at the time the function is created. With this approach each closure will return a different value because they're returning their private local variable (a parameter that has a default).

Confusion regarding mutable and immutable data types in Python 2.7

I have read that while writing functions it is good practice to copy the arguments into other variables because it is not always clear whether the variable is immutable or not. [I don't remember where so don't ask]. I have been writing functions according to this.
As I understand creating a new variable takes some overhead. It may be small but it is there. So what should be done? Should I be creating new variables or not to hold the arguments?
I have read this and this. I have confusion regarding as to why float's and int's are immutable if they can be changed this easily?
EDIT:
I am writing simple functions. I'll post example. I wrote the first one when after I read that in Python arguments should be copied and the second one after I realized by hit-and-trial that it wasn't needed.
#When I copied arguments into another variable
def zeros_in_fact(num):
'''Returns the number of zeros at the end of factorial of num'''
temp = num
if temp < 0:
return 0
fives = 0
while temp:
temp /= 5
fives += temp
return fives
#When I did not copy arguments into another variable
def zeros_in_fact(num):
'''Returns the number of zeros at the end of factorial of num'''
if num < 0:
return 0
fives = 0
while num:
num /= 5
fives += num
return fives
I think it's best to keep it simple in questions like these.
The second link in your question is a really good explanation; in summary:
Methods take parameters which, as pointed out in that explanation, are passed "by value". The parameters in functions take the value of variables passed in.
For primitive types like strings, ints, and floats, the value of the variable is a pointer (the arrows in the following diagram) to a space in memory that represents the number or string.
code | memory
|
an_int = 1 | an_int ----> 1
| ^
another_int = 1 | another_int /
When you reassign within the method, you change where the arrow points.
an_int = 2 | an_int -------> 2
| another_int --> 1
The numbers themselves don't change, and since those variables have scope only inside the functions, outside the function, the variables passed in remain the same as they were before: 1 and 1. But when you pass in a list or object, for example, you can change the values they point to outside of the function.
a_list = [1, 2, 3] | 1 2 3
| a_list ->| ^ | ^ | ^ |
| 0 2 3
a_list[0] = 0 | a_list ->| ^ | ^ | ^ |
Now, you can change where the arrows in the list, or object, point to, but the list's pointer still points to the same list as before. (There should probably actually only be one 2 and 3 in the diagram above for both sets of arrows, but the arrows would have gotten difficult to draw.)
So what does the actual code look like?
a = 5
def not_change(a):
a = 6
not_change(a)
print(a) # a is still 5 outside the function
b = [1, 2, 3]
def change(b):
b[0] = 0
print(b) # b is now [0, 2, 3] outside the function
Whether you make a copy of the lists and objects you're given (ints and strings don't matter) and thus return new variables or change the ones passed in depends on what functionality you need to provide.
What you are doing in your code examples involves no noticeable overhead, but it also doesn't accomplish anything because it won't protect you from mutable/immutable problems.
The way to think about this is that there are two kinds of things in Python: names and objects. When you do x = y you are operating on a name, attaching that name to the object y. When you do x += y or other augmented assignment operators, you also are binding a name (in addition to doing the operation you use, + in this case). Anything else that you do is operating on objects. If the objects are mutable, that may involve changing their state.
Ints and floats cannot be changed. What you can do is change what int or float a name refers to. If you do
x = 3
x = x + 4
You are not changing the int. You are changing the name x so that it now is attached to the number 7 instead of the number 3. On the other hand when you do this:
x = []
x.append(2)
You are changing the list, not just pointing the name at a new object.
The difference can be seen when you have multiple names for the same object.
>>> x = 2
>>> y = x
>>> x = x + 3 # changing the name
>>> print x
5
>>> print y # y is not affected
2
>>> x = []
>>> y = x
>>> x.append(2) # changing the object
>>> print x
[2]
>>> print y # y is affected
[2]
Mutating an object means that you alter the object itself, so that all names that point to it see the changes. If you just change a name, other names are not affected.
The second question you linked to provides more information about how this works in the context of function arguments. The augmented assignment operators (+=, *=, etc.) are a bit trickier since they operate on names but may also mutate objects at the same time. You can find other questions on StackOverflow about how this works.
If you are rebinding the name then mutability of the object it contains is irrelevant. Only if you perform mutating operations must you create a copy. (And if you read between the lines, that indirectly says "don't mutate objects passed to you".)

Hole-in-scope, dead code or why such output?

Code
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = number + 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable
main()
When I read it, I thought it will output 4 8 12 15 but it outputs 4 13 12 15. I can see here that Python deals with integer and lists differently, I assumed that the last thing is impossible without global. I cannot understand the output, in such case, why would it not output 4 13 12 17?
You can see here almost identical code with different types and different reference:
$ python test2.py
4
13
12
15
$ python test3.py
4
13
12
17
$ cat test2.py test3.py
Pass-by-reference examples
test2.py: pass-by-reference and mutable data type -example. Table/list is not enough to affect the local variable in main, you need the Reference!
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
test3.py: pass-by-reference example, changing a mutable data type list/table outside the main function
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number[0] += 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
pass-by-value examples
test4.py: trying to find an example with pass-by-value, why it does not work?
$ cat test4.py
# Not yet a pass-by-value example!
global variable
variable = [15]
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
#variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
$ python test4.py
4
13
12
15 # I expected 17! Why no 17?
def change1(list1):
# `list1[1] =` means you are changing the object passed in
list1[1] = list1[1] + 5
def change2(number):
# `number = ` means you create a **new local variable**, number,
# based on the `number`you passed in
number = [x+2 for x in number]
So if you want to change existing objects, you have to referene them in some way, for example in
def change3(number):
# `number[:]` is the whole existing list and you overwrite it
number[:] = [x+2 for x in number]
Note the [ .. ] when changing a list.
Python parameters are passed by reference. You mutating only one object in change1.
However, numerical values and Strings are all immutable. You cannot change the value of a passed in immutable and see that value change in the caller. Dictionaries and Lists on the other hand are mutable, and changes made to them by a called function will be preserved when the function returns.
More: http://www.penzilla.net/tutorials/python/functions/
The definitive answer is that Python is actually "call by sharing", also known as "call by object" or "call by object reference".
This has been extensively discussed before. From that article:
From time to time, people who’ve read a little CS but not a lot CS (or too much of just one kind of CS) pop up on comp.lang.python and waste a lot of energy trying to tell everyone that Python’s using some calling model that it doesn’t really use. It always turns out that they don’t really understand Python’s model, and quite often, they don’t understand their favourite model either.
But nevermind, the only thing you need to know is that Python’s model is neither “call by value” nor “call by reference” (because any attempt to use those terms for Python requires you to use non-standard definitions of the words “-value” and “-reference”). The most accurate description is CLU’s “call by object” or “call by sharing“. Or, if you prefer, “call by object reference“.
You should also read this, if you haven’t done so already.
Python's semantics are most similar to the semantics of the language CLU. The CLU Reference Manual by Liskov et al describes the semantics like this:
"We call the argument passing technique call by sharing,
because the argument objects are shared between the
caller and the called routine. This technique does not
correspond to most traditional argument passing techniques
(it is similar to argument passing in LISP). In particular it
is not call by value because mutations of arguments per-
formed by the called routine will be visible to the caller.
And it is not call by reference because access is not given
to the variables of the caller, but merely to certain objects."
In change1 you exchange the value in the list with value + 5.
In change2 you add 5 to number. The result is a new object and is not just applied to the passed variable.
If you come from C++: No there is no int& var in Python.
You get the expected result when doing this:
def change2(number):
return number + 5
variable = 15
variable = change2(variable)
If you still don't want to return a value, you could create a MutableInt class.
class MutableInt(object):
def __init__(self, value = 0):
self._value = int(value)
def __add__(self, other):
self._value += int(other)
return self
def __sub__(self, other):
self._value -= int(other)
return self
...
All the examples show call-by-value. Python only has call-by-value. There is no call-by-reference. All values in python are references (it is not possible to have an "object" as the value). Hence it is references that are copied when passed to the function. Lists are mutable, so it is possible to mutate its contents through a shared reference. In change2 you are reassigning a local variable to point to another object, which, like all assignments to local variables, has no effect on any calling scope, since it is call-by-value.

Categories

Resources