0

I'm new, please bear with me. Below is my code:

class Player():
    name = "UnknownName"
    stren = 10
    dex = 10
    con = 10
    intel = 10
    wis = 10
    cha = 10

    def randAssign(self):
        global stren, dex, con, intel, wis, cha
        stat_List = [stren, dex, con, intel, wis, cha]

        for stat in stat_List:
             r_1 = random.randint(1,6)
             r_2 = random.randint(1,6)
             r_3 = random.randint(1,6)
             r_4 = random.randint(1,6)

             stat_Val = r_1 + r_2 + r_3 + r_4 - min(r_1, r_2, r_3, r_4)
             stat = stat_Val

randAssign is a method in Player() I'm trying to set a player's stats randomly, and need to rewrite the class variables at the time randAssign() is activated. For one reason or another, the use of global passes the following error:

NameError: name 'stren' is not defined. 

Using Nonlocal yields this error:

SyntaxError: no binding for nonlocal 'stren' found

Without either global or nonlocal, it just doesn't rewrite Player()'s variables. I've done a dozen iterations of this, from the outright wrong to the "I thought this would work but it doesn't", and need help.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • 3
    why are you trying to use global vars to modify the values inside your class ? To rewrite player variable, you need to modify `self.stren` and assign your new value. – Jean Rostan Apr 12 '18 at 15:47
  • 2
    This is an [XY-Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – timgeb Apr 12 '18 at 15:49

3 Answers3

3

Defining your variables inside your class like this will make all instances of your class share these variables, which can lead to funky situations (especially for mutable objects). (refer to Python documentation) What you most likely want to do is to use this:

class Player:

    def __init__(self):
        self.stren = 10
        self.xcx = 10
        etc.

    def randAssign(self):
        self.stren = randint(1,10)
        ...
Jean Rostan
  • 1,056
  • 1
  • 8
  • 16
  • 1
    Sharing the default values is not so bad (as long as they are immutable objects like integers). – Graipher Apr 12 '18 at 15:54
  • @Graipher it's not bad,it can be useful, but from I try to understand from his problem, this is not the wanted behaviour, he want to have different players with different stats ? – Jean Rostan Apr 12 '18 at 15:55
  • 1
    Since they are immutable, they will be overwritten only for the instance, not all instances. – Graipher Apr 12 '18 at 15:57
  • You only have to be careful if you are doing e.g. `player.some_list.append(something)`. That of course modifies the object shared by all instances, instead of modifying only that instance. – Graipher Apr 12 '18 at 16:00
  • The mutability or immutability is irrelevant to whether they are shared between all instances. They are always shared by instances. Also, @Graipher, mutable and immutable objects will be overwritten just the same. – juanpa.arrivillaga Apr 12 '18 at 16:21
  • @juanpa.arrivillaga True if you are re-assigning them. I just wanted to warn against doing `player.some_list.append(something)`, with `some_list` being a class attribute, and expecting only the instance `player` to be modified. – Graipher Apr 12 '18 at 16:25
  • 1
    @Graipher Unless they deepcopy the class variable to an instance variable, but as I pointed out to juanpa already, OP's issue was on scoping; we've all done more than asked. – pstatix Apr 12 '18 at 16:52
1

Since you already pass the self argument in your function definition, you should refer to these variables in the appropriate namespace:

self.stren = ...

instead of

globals stren
stren = ...
0

Not really sure if you are familiar with class vs instance methods, but try this:

class Player():
    name = "UnknownName"
    stren = 10
    dex = 10
    con = 10
    intel = 10
    wis = 10
    cha = 10

    @classmethod
    def randAssign(cls):
        stat_List = [cls.stren, 
                     cls.dex, 
                     cls.con, 
                     cls.intel, 
                     cls.wis, 
                     cls.cha]

        for stat in stat_List:
             r_1 = random.randint(1,6)
             r_2 = random.randint(1,6)
             r_3 = random.randint(1,6)
             r_4 = random.randint(1,6)

             stat_Val = r_1 + r_2 + r_3 + r_4 - min(r_1, r_2, r_3, r_4)
             stat = stat_Val

Given what it looks like you are doing (creating random Player stats), this would be more approriate if done per Player instance under the __init__ call upon instantiation.

Read this SO for more information on the difference between the two methods.

Edit: To illustrate point in comments...

This is the problem you will encounter with your method (my above example only resolves your scoping issue, not your implementation one)

class Temp:
    stat = 1
    @classmethod
    def test(cls):
        stats = [cls.stat] # container contains copies of primitive types 
        print (stats) # [1]
        for i in range(1):
            stats[i] = (stats[i] + 2) * 3 # 1 + 2 * 3 = 9
        print (stats) # [9]
        print (cls.stat) # 1

To do what comments appear you want to:

class Temp:
    stats = {'stat': 1}
    @classmethod
    def test(cls):
        stats = [cls.stats] # container contains reference to another container
        print (stats) # {'stat': 1}
        for stat in stats:
            cls.stats[stat] = (cls.stats[stat] + 2) * 3 # 1 + 2 * 3 = 9
        print (stats) # {'stat': 9}
        print (cls.stats) # {'stat': 9}, look they updated!
pstatix
  • 3,611
  • 4
  • 18
  • 40
  • This method fails to modify the class variables in exactly the same way as the OPs attempts – juanpa.arrivillaga Apr 12 '18 at 16:24
  • @juanpa.arrivillaga Their problem was scope, I've updated to give example as to what happens given their implementation, but those are two different issues to address for the OP. – pstatix Apr 12 '18 at 16:56