The function is_valid_word should return True if word is in the word_list and is entirely composed of letters available in the hand. Otherwise, returns False. It does not mutate hand or word_list.
def get_frequency_dict(sequence): #helper function
    freq = {}
    for x in sequence:
        freq[x] = freq.get(x, 0) + 1
    return freq
def is_valid_word(word, hand, word_list):
    
    word = word.lower()
    handc = hand.copy()
    freq = get_frequency_dict(word)
    
    if word in word_list:
        for e in word:
            if e in handc and handc[e] >= freq[e]:
                pass
            else:
                return False
        return True
    else:
        return False
print(is_valid_word('caapture', 
{'c': 3, 'a': 1, 'p': 2, 'e': 1, 't': 1, 'u': 1}, 
['caapture', 'hello'])) #example  
I notice that if I use return True instead of pass in the code, it does not read other letters in word. I understand why.
Are there other ways to implement the function without pass?
Also can the multiple else statements be avoided?
Advertisement
Answer
To get rid of the first else negate the conditions changing
if e in handc and handc[e] >= freq[e]:
    pass
else:
    return False
To
if e not in handc and handc[e] < freq[e]:
    return False
For the second else you can just get rid of it, rewriting
if word in word_list:
    ...
    return True
else:
    return False
as
if word in word_list:
    ...
    return True
return False
Since the function always returns early when inside of the if statement (because of the return True), then we don’t have to actually write out the else statement as the only way for us to not return early and move past the if statement is if the word is not in the wordlist. I.e. we get the behavior of an else without have to write out an else.
Also instead of
freq = get_frequency_dict(word)
You can use a counter.
freq = collections.Counter(word)