Part 1

First question

d = {'a': 1,
'b': 2,
'a': 3}  # <-- repeated 'a' key

Counting words

fd2 = {}
for word in words_to_count:
if not word in fd2:
fd2[word] = 0
fd2[word] += 1

or

fd2 = {}
for word in words_to_count:
if not word in fd2:
fd2[word] = 1
else:
fd2[word] += 1

or

fd2 = {}
for word in words_to_count:
fd2[word] = fd2.get(word, 0) + 1

or

fd2 = {}
for word in words_to_count:
fd2.setdefault(word, 0)
fd2[word] += 1

etc.

Part 2

is_empty()

def is_empty(x):
if x:
return False
else:
return True
def is_empty(x):
return len(x) == 0
def is_empty(x):
if x==[] or x=={}:  # <-- overfits to the test cases
return True
else:
return False

word_lengths()

Original:

def word_lengths(words):
"""Return a list of word lengths for each word in words."""
if len(words) > 0:
for word in words:
return len(word)  # <-- return inside loop
# <-- no 'else' case, returns `None` for an empty list

Minimal changes:

def word_lengths(words):
"""Return a list of word lengths for each word in words."""
lengths = []
if len(words) > 0:        # <-- if-test is unnecessary
for word in words:
lengths.append(len(word))
return lengths

Simpler:

def word_lengths(words):
"""Return a list of word lengths for each word in words."""
return [len(word) for word in words]

Observed changes:

def word_lengths(words):
"""Return a list of word lengths for each word in words."""
if len(words) > 0:
for word in words:
return [len(word) for word in words]  # <-- return still inside loop!
else:
return []  # <-- also: return words (subtle bug!)