Search code examples
pythonlistinheritancereturnsubclassing

How to properly create a list subclass that returns a list generated from file in python?


I am running into some problems with subclassing.

I need to create a class that inherits list properties but I need the returned list (self.tiles_pool) to be one that is created when the class is constructed. The list is generated by an external CSV file. I tried creating the class without inheritance but quickly realized there was no way to return my list without creating a separate method to do so. But I need the instance of the class to be a list object (i.e. print the generated list when I print the object). The class is for creating a Tile Pool like the bag of letter tiles in scrabble.

If there is some way of returning my list (self.tiles_pool) without inheritance and without using user defined methods that would be better.

Here is my code so far:

import csv
import random


class TilePool(list):
    def __init__(self):
        list.__init__(self)

        # Open csv file
        with open("tiles.csv") as f:
            # Read csv into list of lists of each lines values
            tiles_csv = csv.reader(f, delimiter=",")

            # Convert the list into a tile pool
            self.tiles_pool = [(line[0], line[1])
                               for line in tiles_csv if len(line[0]) == 1
                               for x in xrange(int(line[2]))]

            del tiles_csv

    def pop(self, tile_count=None):
        assert len(self.tiles_pool) > 0, "# Tile Pool is empty"

        if tile_count is None:
            tile_count = 7

        assert tile_count in xrange(1, 8), "# Tile Count must be between 1 and 7"

        new_tiles = []
        counter = 1
        while len(self.tiles_pool) > 0 and counter <= tile_count:
            rand_choice = random.choice(self.tiles_pool)  # Get a random tile
            new_tiles.append(rand_choice)  # Add it to new_tiles list
            self.tiles_pool.remove(rand_choice)  # Delete it from pool

            counter += 1

        return new_tiles

    def view_pool(self):
        if len(self.tiles_pool) == 0:
            print("# Tile Pool is empty")

        else:
            for tile in self.tiles_pool:
                print("{letter}: {score}".format(letter=tile[0], score=tile[1]))

            print len(self.tiles_pool)

I understand that this implementation may seem strange and I could probably just put this in a function but I am under instruction to make it a class. Any help or advice would be much appreciated. Thanks.


Solution

  • Why subclass list if you don't use it? self is the list you want, there is no need to create a tiles_pool. Since lists can be initialized with a sequence, delay parent __init__ until you can feed it the csv. Your code does some things I don't understand such as duplicating tiles line[2] times, but I figure you know what you are doing there.

    In this sample, I remove tiles_pool in favor of using self and do a few other tweaks like using the "truthiness" of lists (bool([1]) is True while bool([]) is False) instead of using len but it should work the same as the original.

    import csv
    import random
    
    
    class TilePool(list):
        def __init__(self):
            with open("tiles.csv") as f:
                tiles_csv = csv.reader(f, delimiter=",")
                list.__init__(self, (line[0:2]
                               for line in tiles_csv if len(line[0]) == 1
                               for x in xrange(int(line[2]))))
    
    
        def pop(self, tile_count=None):
            # non-empty list is truthy
            assert self, "# Tile Pool is empty"
    
            if tile_count is None:
                tile_count = 7
    
            assert tile_count in range(1, 8), "# Tile Count must be between 1 and 7"
    
            new_tiles = []
            counter = 1
            while self and counter <= tile_count:
                rand_choice = random.choice(self)  # Get a random tile
                new_tiles.append(rand_choice)  # Add it to new_tiles list
                self.remove(rand_choice)  # Delete it from pool
                counter += 1
            return new_tiles
    
        def view_pool(self):
            if not self:
                print("# Tile Pool is empty")
            else:
                for tile in self:
                    print("{letter}: {score}".format(letter=tile[0], score=tile[1]))
                print len(self)
    
    t = TilePool()
    t.pop(3)
    t.view_pool()