-1

I want games to update when check() is called, but as soon as `games += 1`` runs, it creates a new object (a python quirk) rather than modifying the argument. but I want to modify the argument, so how do I do that?

right now wins and games reset to 0 after check() returns


    def check(...games, wins,...):
       ...
            games += 1
            wins += 1 if winner == 'agent' \
                else 0 if winner == 'bot' \
                else 0.5
            ...
            return 1
        return 0

    def play_bot():
        games = 0
        wins = 0
        ...
        if check(...games, wins,...): player = choice([0,1])
        ...

I've been able to find stuff about why python creates a new object, but not a clean way to prevent this.

Saxt
  • 27
  • 5
  • `games` inside `check` is local variable and it has nothing to do with external variable `games` - they have the same name but for python there are two different variables. – furas Aug 19 '19 at 14:30
  • Please take a look at [MRE](https://stackoverflow.com/help/minimal-reproducible-example). I suggest you either revert your last edit, or remove the undefined variables and `...`'s from this one. – luther Aug 19 '19 at 14:31
  • if you want to change it inside function then return new value - `games = check(games)` or eventually use global variables. You can also keep these values in dictionary `config = {"games": 1}` and send dictionary to function - `check(config)` and it will change value in external/global dictionary if you use `config["games"] += 1` inside function – furas Aug 19 '19 at 14:33
  • OK, that's wierd. I can't find the edit history, but I swear I've seen 2 different edits on this question. – luther Aug 19 '19 at 14:36
  • 2
    Why do you return 1 or 0 instead of the new values? Are these supposed to be error codes? If so, you should consider changing your architecture - you seem to be fighting the language by writing code suitable to something like C. – MisterMiyagi Aug 19 '19 at 14:41
  • 2
    in some your comment I see "object-oriented encapsulation" . Do you create classes ? Then you can use `self.` to resolve problems. – furas Aug 19 '19 at 14:43
  • @MisterMiyagi I only want to change player if the game ended. ``choice(...)`` checks for end game, and returns whether the player should be changed. – Saxt Aug 19 '19 at 14:44
  • So the first edit I saw must have been a 2nd version that got reverted... – luther Aug 19 '19 at 14:45
  • @furas in this case, I am not using classes, so ``self`` will not work. I say 'encapsulation' because I am still separating processes by their purpose. I suppose this example is less object-oriented though – Saxt Aug 19 '19 at 14:47
  • @luther the first version of the question included code unrelated to what was really being asked, so I simply snipped it out. – Saxt Aug 19 '19 at 14:48
  • As a general rule, the code in the question should be runnable so we can copy-paste it and work with it in our editor. – luther Aug 19 '19 at 14:54
  • 2
    It's not a "quirk"; `int`s are immutable – chepner Aug 19 '19 at 15:06
  • @chepner in python, sure, but not every language uses the same scoping mechanics. perhaps 'quick' is a bit strong since it's a common feature, but a language design choice nonetheless. – Saxt Aug 19 '19 at 15:08
  • 1
    Actually, the scoping mechanic in question here is pretty universal. A function can't assign to argument variables, because an argument can be any expression, not just a variable. – luther Aug 19 '19 at 15:21

4 Answers4

2

This is possible duplicate of passing variables by reference. I recommend you go through this question.

Also python allows you to return multiple values. You can do

return 1, games

or

return 0, games

By doing this you get a tuple with two elements returned from function. So you won't be able to use it directly inside an if statement. You can store the two values and then compare in if statement.

1

The cleanest way to do this would be to simply return the information you need from the check function:

def check(node, winner):
    if node:
        return 1 if winner == 'agent' else 0 if winner == 'bot' else 0.5
    # Return None by default.

def play_bot():
    games = 0
    wins = 0
    win = check(node, winner)
    if win is not None:
        games += 1
        wins += win
        player = choice([0,1])

If you really need to go the OOP route, you can pass an object to check so that function can modify it as much as it needs to:

class Record:
    def __init__(self):
        self.games = self.wins = 0

def check(node, record, winner):
    if node:
        record.games += 1
        record.wins += 1 if winner == 'agent' else 0 if winner == 'bot' else 0.5
        return True
    return False

def play_bot():
    record = Record()
    if check(node, record, winner):
        player = choice([0,1])

A 3rd solution would be to use global variables, but I really don't recommend that. They can be a real mess.

luther
  • 5,195
  • 1
  • 14
  • 24
1

Number is imutable types. And the games is a copy for the argument games in the funcion. So you can't change it.

If you want change it ,you can put it into a mutable varibles for example list, dictionary.Then you could change it in the function.

ricky.bo
  • 11
  • 2
0

I am no python-expert, but I believe python has immutable and mutable types. The immutable types, when modified within a function, will create a new instance of the object. Immutable types are things like strings, tuples, and numbers. In your case, games, is an immutable type (number).

See: Passing Values in Python

How to circumvent the new object-creation of an immutable type, I do not know. The most obvious solutions to your problem are, use a global variable or return the new games-object.

Matthew E. Miller
  • 557
  • 1
  • 5
  • 13
  • thanks, that would be really helpful. while returning the objects is one option, it would break the object-oriented encapsulation. – Saxt Aug 19 '19 at 14:41