2

I'm trying to create a Python version of Monopoly. I have a separate class that I am using to shuffle and track the Chance and Community Chest cards. The cards are stored in lists chest_cards and chance_cards.

def __init__(self):
    self.chance = random.shuffle(chance_cards)
    self.chest = random.shuffle(chest_cards)
    self.chance_count = 0
    self.chest_count = 0
    
def chance(self):
    self.chance_count += 1
    return self.chance[self.chance_count - 1]

In my main code, I am just running

p = cards()
print (p.chance())

to test my code, but I get TypeError: 'NoneType' object is not callable for the print line.

Any ideas? Or do you need to see more code? TIA

EDIT: Here is the full cards class, if it helps

import random
global chance_count
global chest_count

class cards:
    global chest_cards
    global chance_cards
    chest_cards = (["Go to Jail","Get Out of Jail Free","Advance to Go (Collect $200)",
"Bank error in your favor (Collect $200)","Doctor's fee (Pay $50)", 
"From sale of stock you get $50", "Grand Opera Night — Collect $50 from every player", 
"Holiday Fund matures (Collect $100)", "Income tax refund (Collect $20)",
"It is your birthday (Collect $10)","Life insurance matures (Collect $100)",
"Pay hospital fees of $100", "Pay school fees of $150", "Receive $25 consultancy fee",
"You are assessed for street repairs – $40 per house – $115 per hotel",
"You have won second prize in a beauty contest (Collect $10)", "You inherit $100"])

    chance_cards = (["Go to Jail","Get Out of Jail Free","Advance to Go (Collect $200)",
"Advance to Illinois Ave — If you pass Go, collect $200",
"Advance to St. Charles Place – If you pass Go, collect $200",
"Advance token to nearest Utility. If unowned, you may buy it from the Bank. If owned, throw dice and pay owner a total ten times the amount thrown.",
"Advance token to the nearest Railroad and pay owner twice the rental to which he/she is otherwise entitled. If Railroad is unowned, you may buy it from the Bank.",
"Bank pays you dividend of $50", "Go Back 3 Spaces",
"Make general repairs on all your property – For each house pay $25  –For each hotel $100",
"Pay poor tax of $15","Take a trip to Reading Railroad – If you pass Go, collect $200",
"Take a walk on the Boardwalk – Advance token to Boardwalk", 
"You have been elected Chairman of the Board – Pay each player $50",
"Your building and loan matures — Collect $150", "You have won a crossword competition (Collect $100)"])

    def __init__(self):
        self.chance = random.shuffle(chance_cards)
        self.chest = random.shuffle(chest_cards)
        self.chance_count = 0
        self.chest_count = 0
        
    def chance(self):
        self.chance_count += 1
        return self.chance[self.chance_count - 1]
2
  • What's cards? Commented Jan 24, 2021 at 23:09
  • You can't call attributes and methods by the same name - also random.shuffle changes the list in-place and doesn't return anything. so in your init this sets self.chance = None Commented Jan 24, 2021 at 23:28

2 Answers 2

2

when you create an instance of your class (assuming it's class cards: before your __init__ function), you create an object with a method called chance, but then during __init__ you overwrite this method with an attribute called chance with the return value of random.shuffle which is always None because shuffle "shuffles" the list in-place rather than creating a new list with a random order:

>>> chance_cards = ['card1', 'card2', 'card3']
>>> chance = random.shuffle(chance_cards)
>>> print(chance)
None

EDIT: A note on globals

options to get rid of global (you should really do some outside learning on variable scope on your own...):

  1. Move your variables outside the class into the "module scope". You can still refer to them in your class.
import random, copy #copy isn't strictly needed but used for clarity
# `new_list=some_list[:]` is functionally equivalent to `new_list=copy.copy(some_list)`
chest_cards = (["Go to..."])
chance_cards = (["Go to Ja..."])

class cards:
    def __init__(self):
        self.chest_cards = copy.copy(chest_cards) #make a local copy so you don't alter the master list
        random.shuffle(self.chest_cards)
  1. If you only need them within the class, leave them as a class attribute and refer to them with either self or by the name of the class.
import random, copy
class cards:
    chest_cards = (["Go to..."])
    chance_cards = (["Go to Ja..."])
    def __init__(self):
        #before you over-write self.chest_cards, it refers to the class attribute
        self.chest_cards = copy.copy(self.chest_cards) #make a local copy so you don't alter the master list
        #after you over-write it, it will refer to the instance attribute as long as you made a copy.
        #you can also refer to the class directly to access class-attributes
        self.chest_cards = copy.copy(cards.chest_cards)
        #if you want to get the class without calling it by name (in case you want to change the name of the class)
        self.chest_cards = copy.copy(type(self).chest_cards)
        random.shuffle(self.chest_cards)
  1. There are definitely more ways... see if you can find any :)
Sign up to request clarification or add additional context in comments.

5 Comments

your edit brings some other things up with respect to global variable scope... give me a minute...
Never use globals - your code doesn't need it.
random.shuffle doesn't return a random value from the list. It shuffles the given list in place and returns None.
@TonySuffolk66 I have this bad habit of writing answers from memory before I go check my work for being 100% correct... I suppose I was at least close the first time ;) thanks for the correction
I do the same - no worries.
1

Firstly :

You have a data attribute called chance, and a method called chance; so when you set self.chance in your init method, it overwrites the reference to the method of the same name - come up with a diffferent name for your chance attribute`

Secondly:

random.shuffle() is an in-place function - it changes the list that you pass it, and retuens NONE - which is why your chance attribute is set to None. If you want your chance attribute to be a version of your chance global which is shuffled - then do this :

def __init__(self):
     self.chance = chance[:]
     random.shuffle(self.chance)

or

def __init__(self):
     self.chance = list(random.choices(chance, k=len(chance))

Thirdly:

Globals - why (they are very bad thing to get into the habit of using)- and if you are going to use them (why) don't set from a class body - that is entirely unneccessary, and confusing.

2 Comments

I'm a very novice coder so I'd appreciate any tips on alternate paths! I used the globals because otherwise, I get a name error. How do I create variables in a class that are used throughout the class?
Just includie them in the class inside the class definition, but not within any methods - : docs.python.org/3/tutorial/… When you need to use them in your code use class.chance_cardsor class.chest_cards

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.