String being mutated to a dictionary but can't figure out where
<p>I'm trying to implement a scrabble type  game and the function playHand is returning an AttributeError: 'str' object has no attribute 'copy'. The error comes from updateHand Hand = Hand.copy(), but I've tested updateHand individually and works just fine on its ow. I've tried looking it up, but never really found anything. Apologies for the length! I can't help it. Thanks!</p>

<pre><code>VOWELS = 'aeiou'
CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
HAND_SIZE = 7

SCRABBLE_LETTER_VALUES = {
'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, 'f': 4, 'g': 2, 'h': 4, 'i': 1, 'j': 8, 'k': 5, 'l': 1, 'm': 3, 'n': 1, 'o': 1, 'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1, 'u': 1, 'v': 4, 'w': 4, 'x': 8, 'y': 4, 'z': 10
}

def getWordScore(word, n):
"""
Returns the score for a word. Assumes the word is a valid word.

The score for a word is the sum of the points for letters in the
word, multiplied by the length of the word, PLUS 50 points if all n
letters are used on the first turn.

Letters are scored as in Scrabble; A is worth 1, B is worth 3, C is
worth 3, D is worth 2, E is worth 1, and so on (see SCRABBLE_LETTER_VALUES)

word: string (lowercase letters)
n: integer (HAND_SIZE; i.e., hand size required for additional points)
returns: int &gt;= 0
"""
score = 0

for letter in word:
    score += SCRABBLE_LETTER_VALUES[letter]

return score * len(word) + 50 * (len(word) == n)

def displayHand(hand):
"""
Displays the letters currently in the hand.

For example:
&gt;&gt;&gt; displayHand({'a':1, 'x':2, 'l':3, 'e':1})
Should print out something like:
   a x x l l l e
The order of the letters is unimportant.

hand: dictionary (string -&gt; int)
"""
for letter in hand.keys():
    for j in range(hand[letter]):
         print(letter,end=" ")       # print all on the same line
print()                             # print an empty line


def updateHand(hand, word):
"""
Assumes that 'hand' has all the letters in word.
In other words, this assumes that however many times
a letter appears in 'word', 'hand' has at least as
many of that letter in it. 

Updates the hand: uses up the letters in the given word
and returns the new hand, without those letters in it.

Has no side effects: does not modify hand.

word: string
hand: dictionary (string -&gt; int)    
returns: dictionary (string -&gt; int)
"""

Hand = hand.copy()

for letter in word:
    Hand[letter] -= 1

return Hand



def isValidWord(word, hand, wordList):
"""
Returns True if word is in the wordList and is entirely
composed of letters in the hand. Otherwise, returns False.

Does not mutate hand or wordList.

word: string
hand: dictionary (string -&gt; int)
wordList: list of lowercase strings
"""
Hand = hand.copy()

if word not in wordList:
    return False

for letter in word:
    if letter not in Hand:
        return(False)   
        break
    else: 
        Hand[letter] -= 1  
        if Hand[letter] &lt; 0:
            return False
return True    

def calculateHandlen(hand):
""" 
Returns the length (number of letters) in the current hand.

hand: dictionary (string int)
returns: integer
"""
return sum(hand.values())

def printCurrentHand(hand):
print('Current Hand: ', end = ' ')
displayHand(hand)

def printTotalScore(score):
print('Total: ' + str(score) + ' points')

def updateAndPrintScore(score, word, n):
currentScore = getWordScore(word, n)
score += currentScore
print('" ' + word + ' "' + ' earned ' + str(currentScore) + ' points.', end             = ' ')
print(printTotalScore(score))    

return score


def playHand(hand, wordList, n):
"""
Allows the user to play the given hand, as follows:

* The hand is displayed.
* The user may input a word or a single period (the string ".") 
  to indicate they're done playing
* Invalid words are rejected, and a message is displayed asking
  the user to choose another word until they enter a valid word or "."
* When a valid word is entered, it uses up letters from the hand.
* After every valid word: the score for that word is displayed,
  the remaining letters in the hand are displayed, and the user
  is asked to input another word.
* The sum of the word scores is displayed when the hand finishes.
* The hand finishes when there are no more unused letters or the user
  inputs a "."

  hand: dictionary (string -&gt; int)
  wordList: list of lowercase strings
  n: integer (HAND_SIZE; i.e., hand size required for additional points)

"""
score = 0# Keep track of the total score
hand_copy = hand.copy()

while calculateHandlen(hand_copy) &gt; 0:
    printCurrentHand(hand_copy)
    word = input("Enter word, or a \".\" to indicate that you are finished: ")

    if word == '.':
        print("Goodbye!", end = ' ')
        break

    if isValidWord(word, hand_copy, wordList):
        score += updateAndPrintScore(score, word, n)
        hand_copy = updateHand(word, hand_copy)

    else: 
        print('Invalid word!')
        print()

if calculateHandlen(hand_copy) == 0:
    print("Run out of letters.", end = ' ')

printTotalScore(score)      

playHand({'h':1, 'i':1, 'c':1, 'z':1, 'm':2, 'a':1},['him', 'cam'], 7)
</code></pre>

<p><em>Error code</em></p>

<pre><code>runfile('C:/Users/.spyder2-py3/ProjectsMIT/temp.py', wdir='C:/Users/.spyder2-py3/ProjectsMIT')
Current Hand:  c a h i m m z 

Enter word, or a "." to indicate that you are finished: him
" him " earned 24 points. Total: 24 points
None
Traceback (most recent call last):

  File "&lt;ipython-input-2-2e4e8585ae85&gt;", line 1, in &lt;module&gt;
    runfile('C:/Users/.spyder2-py3/ProjectsMIT/temp.py', wdir='C:/Users/.spyder2-py3/ProjectsMIT')

  File "C:\Users\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 714, in runfile
    execfile(filename, namespace)

  File "C:\Users\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 89, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

    line 183, in &lt;module&gt;
    playHand({'h':1, 'i':1, 'c':1, 'z':1, 'm':2, 'a':1},['him', 'cam'], 7)

    line 172, in playHand
    hand_copy = updateHand(word, hand_copy)

   line 72, in updateHand
    Hand = hand.copy()

AttributeError: 'str' object has no attribute 'copy'
</code></pre>

<p>Your passed-in arguments to <code>updateHand()</code> are reversed from the expected arguments:</p>

<p>line 161:</p>

<pre><code>hand_copy = updateHand(word, hand_copy)
</code></pre>

<p>Line 49:</p>

<pre><code>def updateHand(hand, word):
</code></pre>

<p>Notice that <code>updateHand</code> expects a hand to be the first arg, but you pass a hand as the second arg.</p>

<p>The fix is simple. Replace line 161 with:</p>

<pre><code>hand_copy = updateHand(hand_copy, word)
</code></pre>

python string dictionary semantics