First off, naming, one of the hardest things in Software Development. I would propose a different name than AnalyzeWord
, because it is a word-that-has-been-analyzed object and not an analyzer-of-words object. Maybe something like PhoneticWord
, EspeakWord
or simply Word
might better describe this object.
Next, you call super()
, even though you don't have any base-class (except object
, implicitly, since you are using Python 3.x). So you don't need it at all.
Regarding the better storing of the IPA vowels, I would first make it a global constant (it does not change between instances of the class. If you want to you could also make it a class constant, same difference. Next, I would make it a set
, for which membership testing is \$\mathcal{O}(1)\$ instead of \$\mathcal{O}(n)\$ as it is for a list
.
In your check_rhyme
function, I would add a if not isinstance(word, AnalyzeWord)
check around the casting of the other word. This way you could pass in an already analyzed word. I would also make the function return the match percentage and let the caller do the printing, if they want to. In addition, returning a float
here, instead of an int
gives the caller even more power. If he really wants an int, let them do the casting (or, if you use this feature, together with the printing, often enough, write another function that does just that).
For the printing itself, I would use str.format
.
You should add a if __name__ == '__main__':
guard to your testing code so it will not be executed when you do e.g. import ip_word_analyzeer
in another script.
from difflib import SequenceMatcher
from subprocess import check_output
class Word:
"""
Uses espeak's output to get a word's phonetics & syllables.
"""
ipa_vowels = {'ɐ', 'i', 'ɪ', 'e', 'ɛ', 'æ', 'a', 'ə',
'ɑ', 'ɒ', 'ɔ', 'ʌ', 'ɚ', 'o', 'ʊ', 'u', 'eɪ', 'aɪ'}
cmd = ['espeak', '-q', '-v', 'en-us', '--ipa']
def __init__(self, arg):
self.arg = arg
self.word = arg
self.phon = list(check_output(
Word.cmd + [self.arg]).decode('UTF-8').strip())
for i, x in enumerate(self.phon):
if x == 'ɪ' and self.phon[i - 1] in Word.ipa_vowels:
self.phon[i - 1:i + 1] = [''.join(self.phon[i - 1:i + 1])]
self.syll = len([l for l in self.phon if l in Word.ipa_vowels])
def check_rhyme(self, word):
if not isinstance(word, Word):
word = Word(word)
sm = SequenceMatcher(None, self.phon, word.phon)
return sm.ratio() * 100
def compare_words(word1, word2):
if not isinstance(word1, Word):
word1 = Word(word1)
perc = word1.check_rhyme(word2)
if perc > 50:
print("Good rhyme - {}%".format(perc))
else:
print("Not a good rhyme - {}%".format(perc))
if __name__ == '__main__':
test1 = Word('steed')
compare_words(test1, 'speed')
Maybe with some more thought (and an example containing a 'ɪ'
vowel) that loop could be improved as well.
If you want to go slightly further, you could make this Word
class inherit from str
, giving it a __str__
, __add__
, __len__
and so on function. Or you could implement sensible things for that, maybe like so (here I actually made a new class inheriting from Word
, so I don't have to re-implement the constructor and such.:
class FancyWord(Word, str):
def __repr__(self):
return "".join(self.phon)
def __str__(self):
return self.word
def __add__(self, other):
return FancyWord(str(self) + str(other))
def __len__(self):
return self.syll
def __iter__(self):
return iter(self.phon)
With this you can now do these things:
>>> x = FancyWord("speed")
>>> print(x)
speed
>>> x
spˈiːd
>>> repr(x)
spˈiːd
>>> len(x)
1
>>> for phoneme in x: print(phoneme)
s
p
ˈ
i
ː
d
>>> y = FancyWord("steed")
>>> x + y
spˈiːdstiːd
>>> " ".join([x, y])
'speed steed'
The last one works, because of multiple inheritance magic, so basically the second base class str
of FancyWord her. Word
inheriting from str
, as I suggested bove and then implementing these methods would also work.
This might help you with whatever you want to further do with this class.
if
clause in thefor
loop actually triggers. \$\endgroup\$if
in thatfor
loop the class would indicate "able" has three vowel sounds, but really it's just 'eɪ' \$\endgroup\$