简体   繁体   中英

Why is it returning TypeError: object of type 'NoneType' has no len()

Python 3.8.2 Mac Big Sur 11.2.1

This code is supposed to be a simple minesweeper game in the terminal but when i run it i only get a error message (shown below) i couldn't find any help to this problem when searching around so i thought to ask it myself instead

import random
import re

class Board:
    def __init__(self, dim_size, num_bombs):
        self.dim_size = dim_size
        self.num_bombs = num_bombs

        self.board = self.make_new_board() 
        self.assign_values_to_board

        self.dug = set() 

    def make_new_board(self):
        board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]

    def assign_values_to_board(self):
        for r in range(self.dim_size):
            for c in range(self.dim_size):
                if self.board[r][c] == '*':
                    continue
                self.board[r][c] = self.get_num_neighboring_bombs(r, c)

    def get_num_neighboring_bombs(self, row, col):
        num_neighboring_bombs = 0
        for r in range(max(0, row-1), min(self.dim_size-1, row+1)+1):
            for c in range(max(0, col-1), min(self.dim_size - 1, col+1)+1):
                if r == row and c == col:
                     continue
                if self.board[r][c] == '*':
                    num_neighboring_bombs += 1

        return num_neighboring_bombs

        bombs_planted = 0
        while bombs_planted < self.num_bombs:
            loc = random.randint(0, self.dim_size**2 - 1)
            row = loc // self.dim_size
            col = loc % self.dim_size

            if board [row][col] == '*':
                # bomb already plated here
                continue

            board[row][col] = '*' # plant the bomb
            bombs_planted += 1

        return board

    def dig(self, row, col):
        self.dug.add((row, col))

        if self.board[row][col] == '*':
            return False
        elif self.board[row][col] > 0:
            return True

        for r in range(max(0, row-1), min(self.dim_size-1, row+1)+1):
            for c in range(max(0, col-1), min(self.dim_size - 1, col+1)+1):
                if (r, c) in self.dug:
                    continue
                self.dig(r, c)

        return True

    def __str__(self):
        visible_board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
        for row in range(self.dim_size):
            for col in range(self.dim_size):
                if (row,col) in self.dug:
                    visible_board[row][col] = str(self.board[row][col])
                else: 
                    visible_board[row][col] = ' '

            string_rep = ''

            widths = []
            for idx in range(self.dim_size):
                columns = map(lambda x: x[idx], visible_board)
                widths.append(
                    len(
                        max(columns, key = len)
                    )
                )

            indices = [i for i in range(self.dim_size)]
            indices_row = '   '
            cells = []
            for idx, col in enumerate(indices):
                format = '%-' + str(widths[idx]) + "s"
                cells.append(format % (col))
            indices_row += '  '.join(cells)
            indices_row += '  \n'

            for i in range(len(visible_board)):
                row = visible_board[i]
                string_rep += f'{i} |'
                cells = []
                for idx, col in enumerate(row):
                    format = '%-' + str(widths[idx]) + "s"
                    cells.append(format % (col))
                string_rep += ' |'.join(cells)
                string_rep += ' |\n'

            str_len = int(len(string_rep) / self.dim_size)
            string_rep = indices_row + '-'*str_len + '\n' + string_rep + '-'*str_len

            return string_rep

def play(dim_size=10, num_bombs=10):
    board = Board(dim_size, num_bombs)
    safe = True

    while len(board.dug) < board.dim_size ** 2 - num_bombs:
        print(board)
        user_input = re.split(',(\\s)*', input("Where would you like to dig? Input as row,col: "))
        row, col = int(user_input[0]), int(user_input[-1])
        if row < o or row >= board.dim_size or col < 0 or col >= dim_size:
            print("Invalid location. Try again")
            continue

        safe = board.dig(row, col)
        if not safe:
            break

    if safe:
        print("Victory Royale!")
    else:
        print("You suck. Game Over")
        board.dug = [(r,c) for r in range(board.dim_size) for c in range(board.dim_size)]
        print(board)

if __name__ == '__main__':
    play()

When i run this code it only returns

Traceback (most recent call last):
  File "/Users/henry/Documents/projects/GamesPython/Minesweeper/game.py", line 134, in <module>
    play()
  File "/Users/henry/Documents/projects/GamesPython/Minesweeper/game.py", line 115, in play
    print(board)
  File "/Users/henry/Documents/projects/GamesPython/Minesweeper/game.py", line 82, in __str__
    max(columns, key = len)
TypeError: object of type 'NoneType' has no len()

How can i fix this problem

The iterable columns has at least one None value in it. Here is a simple example that provides the same error given the same line of code:

columns = [ "a", None, "b" ]
max(columns, key = len)

Result:

max(columns, key = len)
TypeError: object of type 'NoneType' has no len()

As to why that None value is in there, columns is built from visible_board . I haven't spent the time to understand your logic completely, but I will point out that the line of code that builds visible_board is very suspicious:

visible_board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]

Sure looks to me like it's going to build a list with a lot of None entries in it. That is verified when running your code in a debugger. At the point of the error, visible_board looks like this:

[[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]]

So it isn't surprising that you end up trying to cal the len attribute on a None value and get the error that you do.

What seems to be happening here is that one of the variables you are passing to max() is not defined (that is what NoneType usually means -- something has no type (eg. it's a null value), so you can't get the length of it). Maybe you have forgotten to return something from one of your functions, or perhaps there is a scope error you aren't expecting. It's really hard to tell from your error stack because there are no line numbers in your code and you use max() a bunch of times.

This is pretty complicated, but not that "large," and you have divided your logic nicely into functions. My suggestion would be start writing some unit tests, and probably I'd use doctest for something like this. That should help you narrow-down the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM