How to deal with NoneType error in Python?
Question:
I have only picked up coding and Python in the past week so this question may seem obvious.
I am trying to create a card game. I have already created my deck of cards and now I’m trying to make a function to remove a card from the deck and to test that I am printing out the deck of cards.
However, when I remove a random card from the deck and then try to print out the properties of the remaining cards in the deck I receive an error.
I created a class called Card with attributes name, value, suit
I took out parts of the code which were very long and not exactly relevant.
I have a list of all my cards called the_deck
I tried to account for the error with
"if the_deck[i] is None:continue:"
but that doesn’t seem to work.
Here is my code and then the error.
def pick_a_card():
a = random.choice(the_deck)
print(a.name + " of " + a.suit)
the_deck = the_deck.remove(a)
i = 0
while i <= 51:
if the_deck[i] is None:
continue
else:
print(the_deck[i].name + " of " + the_deck[i].suit)
i+=1
pick_a_card()
The error I get is
TypeError: ‘NoneType’ object is not subscriptable
This error comes from removing a card from the deck. The rest of the function has no errors.
How do I fix this?
Thanks.
Answers:
Solutions to every issue:
- treat
remove()
as an action, not a parser
- use fstrings to concat string data
- separate game logic from diagnostics
- name things what they are
- use a
for
loop to iterate the deck so you never have to worry about how many cards there are
The below script is an example of how to implement the solution list
from collections import namedtuple
import random
#a simple deck generator so this example is complete
Card = namedtuple('Card', 'face suit')
def deck_generator():
faces = [*list(map(str, range(2,11))), 'J', 'Q', 'K', 'A']
suits = ['♠', '♥', '♦', '♣']
deck = []
for s in suits:
for f in faces:
deck.append(Card(f, s))
return deck
Deck = deck_generator()
#end deck generator
the_deck = Deck[:] #unique copy so you don't have to keep making the deck
#game logic
def pick_a_card():
card = random.choice(the_deck)
the_deck.remove(card) #action not parser
return card #not 'a'
#diagnostic separated from game logic
def viewdeck():
deck = ''
for card in the_deck: #number of remaining cards is irrelevant
deck = f'{deck}{card.face}{card.suit}, ' #fstring
print(deck)
card = pick_a_card()
print(f'{card.face}{card.suit}') #fstring
#5♣
viewdeck()
#2♠, 3♠, 4♠, 5♠, 6♠, 7♠, 8♠, 9♠, 10♠, J♠, Q♠, K♠, A♠,
#2♥, 3♥, 4♥, 5♥, 6♥, 7♥, 8♥, 9♥, 10♥, J♥, Q♥, K♥, A♥,
#2♦, 3♦, 4♦, 5♦, 6♦, 7♦, 8♦, 9♦, 10♦, J♦, Q♦, K♦, A♦,
#2♣, 3♣, 4♣, 6♣, 7♣, 8♣, 9♣, 10♣, J♣, Q♣, K♣, A♣,
Aside
Most container types have inline methods that work on the container directly. These methods may not return anything. You could perceive this as an action you are telling the container to perform on itself. Knowing if an inline method will or will not have a return comes with experience or a quick trip through some docs. As an example, consider remove()
, append()
or insert()
versus pop()
. All of these are inline methods, but only pop()
returns data.
I am not sure what your data looks like but this is an example of how to remove a random from a list that may help.
the_deck = [*range(1, 53, 1)]
def remove_val(the_deck):
a = random.choice(the_deck)
the_deck.remove(a)
print('Value removed:', a)
print('New deck:', the_deck)
A point of note in your example is that, that @alaniwi pointed out, is that should not re-assign the_deck = the_deck.remove(a)
rather it should read the_deck.remove(a)
. Also, you are not handling the fact that len(the_deck)
reduces by 1 every time you remove a card.
The remove
list method removes the element passed to by modifying the list. That is, it just modifies the list referenced by the list name. However it returns None
. If there is no return
statement in function/method, Python interpreter returns None
.
To check that remove
method returns None
, run the following code at Python interpreter:
>>> a = [1, 2 ,3]
>>> if a.remove(1) == None:
... print('Returns None')
...
the if
block will be executed.
You can also check what type of error Python will return if you try to index and int
or None
object.
>>> a = None
>>> a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
How do I deal with this error: KeyError: "None of [Index([‘Primary Type’], dtype=’object’)] are in the [columns]"
I have only picked up coding and Python in the past week so this question may seem obvious.
I am trying to create a card game. I have already created my deck of cards and now I’m trying to make a function to remove a card from the deck and to test that I am printing out the deck of cards.
However, when I remove a random card from the deck and then try to print out the properties of the remaining cards in the deck I receive an error.
I created a class called Card with attributes name, value, suit
I took out parts of the code which were very long and not exactly relevant.
I have a list of all my cards called the_deck
I tried to account for the error with
"if the_deck[i] is None:continue:"
but that doesn’t seem to work.
Here is my code and then the error.
def pick_a_card():
a = random.choice(the_deck)
print(a.name + " of " + a.suit)
the_deck = the_deck.remove(a)
i = 0
while i <= 51:
if the_deck[i] is None:
continue
else:
print(the_deck[i].name + " of " + the_deck[i].suit)
i+=1
pick_a_card()
The error I get is
TypeError: ‘NoneType’ object is not subscriptable
This error comes from removing a card from the deck. The rest of the function has no errors.
How do I fix this?
Thanks.
Solutions to every issue:
- treat
remove()
as an action, not a parser - use fstrings to concat string data
- separate game logic from diagnostics
- name things what they are
- use a
for
loop to iterate the deck so you never have to worry about how many cards there are
The below script is an example of how to implement the solution list
from collections import namedtuple
import random
#a simple deck generator so this example is complete
Card = namedtuple('Card', 'face suit')
def deck_generator():
faces = [*list(map(str, range(2,11))), 'J', 'Q', 'K', 'A']
suits = ['♠', '♥', '♦', '♣']
deck = []
for s in suits:
for f in faces:
deck.append(Card(f, s))
return deck
Deck = deck_generator()
#end deck generator
the_deck = Deck[:] #unique copy so you don't have to keep making the deck
#game logic
def pick_a_card():
card = random.choice(the_deck)
the_deck.remove(card) #action not parser
return card #not 'a'
#diagnostic separated from game logic
def viewdeck():
deck = ''
for card in the_deck: #number of remaining cards is irrelevant
deck = f'{deck}{card.face}{card.suit}, ' #fstring
print(deck)
card = pick_a_card()
print(f'{card.face}{card.suit}') #fstring
#5♣
viewdeck()
#2♠, 3♠, 4♠, 5♠, 6♠, 7♠, 8♠, 9♠, 10♠, J♠, Q♠, K♠, A♠,
#2♥, 3♥, 4♥, 5♥, 6♥, 7♥, 8♥, 9♥, 10♥, J♥, Q♥, K♥, A♥,
#2♦, 3♦, 4♦, 5♦, 6♦, 7♦, 8♦, 9♦, 10♦, J♦, Q♦, K♦, A♦,
#2♣, 3♣, 4♣, 6♣, 7♣, 8♣, 9♣, 10♣, J♣, Q♣, K♣, A♣,
Aside
Most container types have inline methods that work on the container directly. These methods may not return anything. You could perceive this as an action you are telling the container to perform on itself. Knowing if an inline method will or will not have a return comes with experience or a quick trip through some docs. As an example, consider remove()
, append()
or insert()
versus pop()
. All of these are inline methods, but only pop()
returns data.
I am not sure what your data looks like but this is an example of how to remove a random from a list that may help.
the_deck = [*range(1, 53, 1)]
def remove_val(the_deck):
a = random.choice(the_deck)
the_deck.remove(a)
print('Value removed:', a)
print('New deck:', the_deck)
A point of note in your example is that, that @alaniwi pointed out, is that should not re-assign the_deck = the_deck.remove(a)
rather it should read the_deck.remove(a)
. Also, you are not handling the fact that len(the_deck)
reduces by 1 every time you remove a card.
The remove
list method removes the element passed to by modifying the list. That is, it just modifies the list referenced by the list name. However it returns None
. If there is no return
statement in function/method, Python interpreter returns None
.
To check that remove
method returns None
, run the following code at Python interpreter:
>>> a = [1, 2 ,3]
>>> if a.remove(1) == None:
... print('Returns None')
...
the if
block will be executed.
You can also check what type of error Python will return if you try to index and int
or None
object.
>>> a = None
>>> a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
How do I deal with this error: KeyError: "None of [Index([‘Primary Type’], dtype=’object’)] are in the [columns]"