Advanced Python
Projects, Session 6

6.1 class Deck. Create a class, Deck, which starts with a list of all 52 cards, and allows the user to "pick a card" from the deck, after which the deck will no longer contain the card. The class also allows for shuffling (randomly reordering) of the deck.
This is just a demo, but to test your solution please use the longer example at bottom:
d = Deck()           # generates a 52-card deck

card = d.pick()      # str, 'Ace of Hearts' (top card)

card2 = d.pick_any() # str, '5 of Clubs' (random card)

d.shuffle()          # None (shuffles cards internally)

card3 = d.pick()             # str, '9 of Spades' (top card,
                             #      now random because of shuffle)


Generating the deck as a list of strings can be accomplished with this code. (It is recommended to try this in isolation and view the results before proceeding). This code should be contained within the class (except for imports). (Since the deck is supposed to be created each time you construct a new Deck instance, this code should be in the __init__() method.)
suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']
ranks = ['Ace', '2', '3', '4', '5', '6', '7', '8',
         '9', '10', 'Jack', 'Queen', 'King']

deck = []

for suit in suits:
    for rank in ranks:
        card = f"{rank} of {suit}"
        deck.append(card)

The nested 'for' loop means we will see every pair combination from the two lists in the new list.

Picking the 'top' card can be accomplished by simply popping the first item from the internal list of cards. This removes and returns the first item from the list.
creturs = ['bear', 'vole', 'muskrat', 'bobcat', 'fox']

item = creturs.pop(0)    # str, 'bear' (first item)

print(creturs)           # ['vole', 'muskrat', 'bobcat', 'fox']


Picking a random card can be accomplished with the random.choice() function:
import random

fruits = ['apple', 'banana', 'orange', 'pear']

snack = random.choice(fruits)       # str, 'orange' (randomly selected)


'Shuffling' (random reordering) of a list can be accomplished with the random.shuffle() function:
fruits = ['apple', 'banana', 'orange', 'pear']

fruits = random.shuffle(fruits)   # ['orange', 'banana', 'pear', 'apple']
                                  # (random order)


We can remove a particular item from the list using the list .remove() method:
fruits.remove(snack)

print(fruits)         # ['banana', 'pear', 'apple']


Your class should be added above the below code, and should run similarly to as shown (remember that the cards will appear in random order each time you run the program).

print('generating new deck...')
print()

d = Deck()

print('picking card from deck 53 times...')
print()

for i in range(53):
    card = d.pick()  # should show all cards, in order
    print(card)      # when all the cards are gone, must return None


if card is not None:
    print('error:  .pick() must return the None value (not ' +
          "string 'None')")
    exit()

print()

print('generating a new deck...')

e = Deck()


print('picking first card from new deck...')
print()

ecard = e.pick()
print(ecard)           # should show Ace of Hearts
print(); print()


print('picking random card from new deck...')
print()

ecard = e.pick_any()
print(ecard)           # should show random card
print(); print()


print('shuffling...')
print()

e.shuffle()            # reorder cards internally


print('picking first card from the shuffled deck ...')
print()

ecard = e.pick()
print(ecard)           # should show random card
print(); print()

print('attempting to pick from old deck again...')
print()

dcard = d.pick()
print(dcard)           # must be None
Sample program run:
generating new deck...

picking card from deck 53 times...

Ace of Hearts
2 of Hearts
3 of Hearts
4 of Hearts
5 of Hearts
6 of Hearts
7 of Hearts
8 of Hearts
9 of Hearts
10 of Hearts
Jack of Hearts
Queen of Hearts
King of Hearts
Ace of Diamonds
2 of Diamonds
3 of Diamonds
4 of Diamonds
5 of Diamonds
6 of Diamonds
7 of Diamonds
8 of Diamonds
9 of Diamonds
10 of Diamonds
Jack of Diamonds
Queen of Diamonds
King of Diamonds
Ace of Clubs
2 of Clubs
3 of Clubs
4 of Clubs
5 of Clubs
6 of Clubs
7 of Clubs
8 of Clubs
9 of Clubs
10 of Clubs
Jack of Clubs
Queen of Clubs
King of Clubs
Ace of Spades
2 of Spades
3 of Spades
4 of Spades
5 of Spades
6 of Spades
7 of Spades
8 of Spades
9 of Spades
10 of Spades
Jack of Spades
Queen of Spades
King of Spades
None

generating a new deck...
picking first card from new deck...

Ace of Hearts


picking random card from new deck...

Queen of Clubs


shuffling...

picking first card from the shuffled deck ...

6 of Spades


attempting to pick from old deck again...

None


HOMEWORK CHECKLIST please use this checklist to avoid common mistakes and ensure a correct solution:

    the program works exactly as shown in the sample output
    please make sure that any variables that are not expected to live past the life of the function are not set in self. Only values which need to be present for the next method call should be stored as attributes in self.
    there are no extraneous comments or "testing" code lines
    there are no extraneous comments or "testing" code lines
    program follows all recommendations in the "Code Quality" handout

 
6.2 List of a Maximum Size. Create a class, MaxSizeList, that constructs instances that have a list as an attribute (set as an empty list in __init__). The constructor takes an integer as an argument, which becomes the maximum size of the list (stored as a second attribute).

Add a method, push(), which appends a value to the list. However, if the list has already reached its maximum size (as set in the constructor), remove the first element from the list before appending the new element to the list. (You can use the list method pop() with an argument of 0 to remove the first element of the list, or you can use a slice (alist = alist[1:]).)

Sample code to use the class and expected output:
a = MaxSizeList(3)
b = MaxSizeList(1)

a.push("hey")
a.push("ho")
a.push("let's")
a.push("go")

b.push("hey")
b.push("ho")
b.push("let's")
b.push("go")

print(a.get_list())     # ['ho', "let's", 'go']
print(b.get_list())     # ['go']

(hey! ho! let's go! is a reference to a song by the Ramones)
Extra Credit: allow the class to keep track of how many instances were created. This count of instances will be available through the class as well as each instance:

print(MaxSizeList.icount)  # 2


HOMEWORK CHECKLIST please use this checklist to avoid common mistakes and ensure a correct solution:

    the program works exactly as shown in the sample output
    there are no extraneous comments or "testing" code lines
    program follows all recommendations in the "Code Quality" handout

 

EXTRA CREDIT / SUPPLEMENTARY

6.3 (extra credit / supplementary) class Text.

class Text reads a file and exposes (i.e., makes available) methods for reading and inspecting the file. The file should be read only once, in the constructor. The methods manipulate the file data as specified, but do not attempt to read the file again.

t = Text('myfile.txt')      # reads entire file into memory

print(t.filename)              # myfile.txt

text = t.get_text()            # returns entire file as string

bytes = t.get_size()           # returns count of characters in file (use len())

words_list = t.get_words()     # returns file split into list of words

lines_list = t.get_lines()     # returns file as list of lines

found_lines = t.grep('horse')  # returns only lines from file that
                               # contain the search term (use 'in')

word_dict = t.freq()           # returns a dict of unique words paired
                               # with each word's integer frequency
                               # in the file

# have other ideas for what methods might be useful?  include them!


HOMEWORK CHECKLIST please use this checklist to avoid common mistakes and ensure a correct solution:

    file is read only once, in the constructor
    there are no extraneous comments or "testing" code lines
    program follows all recommendations in the "Code Quality" handout

 
6.4 (extra credit / supplementary) Python API for Alpha Vantage. This optional assignment is designed to inspire you to consider how you might design a simplified interface to a service or data source. You may implement the example below, or substitute another file or service that interests you.

The Alphavantage service is relatively easy to use, but it still requires that you remember how the data is structured and to write the necessary JSON or CSV parsing code. If you plan to access the service frequently from a Python program, it's best to write a module of functions, or class of methods, to make access convenient. Sample interface: again, you can design your own interface to whatever service, or part of the AA service, that you choose. You must implement at least two methods.

ibm = Prices('IBM', '15min', 'abc123')     # ticker, interval, API key

print(ibm.symbol)           # IBM

print(ibm.interval)         # 15min

print(ibm.last_refreshed)   # 2020-10-19 20:00:00

print(ibm.last('close'))    # 121.8900

print(ibm.series('close'))  # ["121.8900", "121.8000", "121.9300" ...]


HOMEWORK CHECKLIST please use this checklist to avoid common mistakes and ensure a correct solution:

    data is read only once, in the constructor
    there are no extraneous comments or "testing" code lines
    program follows all recommendations in the "Code Quality" handout

 
[pr]