Machine Learning Rock Paper Scissors Project

Hello

In this project, the file RPS.py shows an example function . The example function is defined with two arguments.

def player(prev_play, opponent_history = ):

Could anyone help me to understand why the function variable “opponent_history” managed to continue tracking the history when it has been called with in the for loop? I would expecting it to be an empty list every time when the function is called. But it almost acted as a global variable which continue append between each for loop!

def player(prev_play, opponent_history=[]):
    opponent_history.append(prev_play)

    guess = "R"
    if len(opponent_history) > 2:
        guess = opponent_history[-2]

    return guess

https://repl.it/@freeCodeCamp/fcc-rock-paper-scissors#README.md

By adding print('opponent_history:', opponent_history) to the function we can see the list continue to expand between each call.

the first time player(prev_play) is called it uses p?_prev_play as the argument

at the end of every loop in play() in RPS_game, the new result of the last player() call is stored by rewriting p?_prev_play

so the next time player() is called, it takes the previously stored p?_prev_play and appends it to opponent_history in the newly called function

…um, i think that makes sense. let me know if it doesnt

Thank you alkapwn3d . I was thinking the same way as well. However this is not the case!
As you can see the screenshot, this is the 4th call within the for loop, the prev_play just capture the last result, not the history.

ugh…youre right. that makes no sense

From this screenshot you can clear see, the variable has never been destroyed within the for loop!

So, this is a bit counter-intuitive at first, and it took me some reading to figure out what is going on.

In Python, functions are first class objects. What this means is that a function can hold data, just like any other object. The default parameter is a piece of data held by its function, and this data is mutable in certain cases.

Here is a smaller example you can play around with:

2 Likes

Thank you JeremyLT! Really appreciate the example. Do you know why the following example, the s variable doesn’t behave the same way as the default list parameter?

def foo(x, history=[]):
    history.append(x)
    print(history)
foo("a")
foo("b")
history

In [27]: foo("c")
['a', 'b', 'c']

In [28]: foo
Out[28]: <function __main__.foo(x, history=['a', 'b', 'c'])>

In [29]: def bar(x, s=""):
    ...:     s +=x
    ...:     print(s)
    ...:

In [30]: bar("a")
a

In [31]: bar("sdfs")
sdfs

In [32]: bar
Out[32]: <function __main__.bar(x, s='')>

I think here is the answer! https://docs.python-guide.org/writing/gotchas/

Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.

2 Likes

After reading your answer again, it financially make sense now. The list argument is mutable and it evaluated when the function was defined. This is why it didn’t work with the bar func when the argument is string. Thank you!

2 Likes

I learned a lot from your question and your experience working through the problem. IT was a great read.

Thanks for sharing

2 Likes