I’d put it in the main program, but there are several things to comment
on here.
First up is that your 2 approaches produce different outcomes.
The first flavour does this:
if w in data:
return data[w]
else:
return "The word doesn't exist"
In both cases the function returns a string. The calling function does
not know if the word was found or not unless it treats the string
"The word doesn't exist"
as a special value. So we almost never do
exactly this.
If you’re taking this approach we usually return a “sentinel value”, a
special value which is clearly not in the range of the function i.e. a
value which is identifiably not a valid return value for a good
translation.
In Python that sentinel value is often the special value None
, which
you test for like this:
translated = translate(word)
if translated is None:
print("Word doesn't exist")
else:
print(translated)
The None
value is not a string at all.
The code to produce it like look like:
if w not in data:
return None
return data[w]
or even better:
return data.get(w)
which as it happens will return None
if w
is not present.
However, returning a sentinel and checking for it is kind of tedious. If
you don’t test this value immediately perhaps it’s going to be stored.
Example:
translated_words = []
for word in words:
translated_words.append(translate(word))
and maybe end up with a list like:
["bonjour", "au revoir", None]
You can identify the sentinel later when it’s important, perhaps. Or you
might just let it all run and come out with rubbish (incomplete
translation) later.
A more common approach in Python is to use exceptions. This is more like
your second approach:
def translate(w):
return data[w]
Here you unconditionally look up w
in data
. If it isn’t there, this
code will raise a KeyError
exception.
In the calling code you test for this like this:
try:
translated_word = translate(word)
except KeyError:
# the word is unknown
print("Word doesn't exist")
else:
print(translated_word)
Cumbersome, eh?
Howver, a primary convenience of an exception is that you don’t need to
check for it right where translate()
is called. Maybe you’ve got code
like this:
def translate(w):
return data[w]
def translate_sentence(sentence):
translated_words = []
for word in sentence.split():
translated_words.append(translate(word))
return translated_words
and in your main program:
sentence = input("Enter a sentence: ")
try:
translated_words = translate_sentence(sentence)
except KeyError:
print("unknown words in the sentence")
else:
print(translated_words)
Even though the exception is raised 2 calls deep in translate()
you
can defer handling it until all the way out in the main program, where
you have the high level view of what you’re doing and maybe do a higher
level response (such as complaining about the sentence as a whole
instead of complaining about each unknown word).
I often think if this kind of thing as “mechanism” versus “policy”.
Mechanism is things like translate()
, which looks up a word in a
table. It does exactly that with no frills. Policy is deciding how to
respond to circumstances. That response often needs a high level view of
what’s going on. As such, policy tends to belong further out in the
program, eg in your main code.
Exceptions let us defer the response to a suitable outer layer of code
(about which the inner low level code like translate()
knows
nothing).