Just complementing, and being a little pedantic about the title ("Indicate whether a character is a vowel or a consonant"): I understand that the function should return True
if you receive a vowel, and False
if you receive a consonant, but what if it receives a string like '&'
, '2'
or ','
? Must return False
also? Because in these cases, the characters are not vowels or consonants, so I understand that should not return True
nor False
- maybe throw an exception then?
Anyway, one option would be to:
def vogal_ou_consoante(letra):
letra = letra.lower()
if letra in ('a', 'e', 'i', 'o', 'u'):
return True
elif letra in ('b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'):
return False
else:
raise ValueError('Não é vogal nem consoante')
print(vogal_ou_consoante('a')) # True
print(vogal_ou_consoante('B')) # False
print(vogal_ou_consoante('2')) # ValueError
Another detail is that I used tuples instead of strings on operator in
. Of course if the string is 'a'
, then do letra in 'aeiou'
works. But what if we have this:
def vogal(v):
return v.lower() in "aeiou"
print(vogal('eio')) # True
Note that if the string is 'eio'
, the return is also True
, since the operator in
, when applied to strings, checks if one string is substring the other.
It’s okay that 'eio'
only has vowels, but if the goal is to verify that a string of any size contains only vowels, then it should also validate 'oi'
and 'uia'
, but these cases return False
. So if we want the function to validate only if the string has only one character (and that character must be vowel or consonant), then we should use the first solution above with ('a', 'e', 'i', 'o', 'u')
.
And the accents?
If I have the string 'á'
(letter "a" with acute accent), it is also a vowel, right? But the above function only considers letters not accented.
A solution would be to add the accented letters in the options, something like letra in ('a', 'e', 'i', 'o', 'u', 'á', 'é', 'í', ... )
(not forgetting to put options in the consonants as well, to include characters such as 'ç'
and the 'ñ'
).
Another option is to normalize the strings to the NFD form. To understand in detail what this is, I suggest you read here, here and here. But in summary, when normalizing to NFD, letters like "á" are decomposed into two characters: the letter "a" (without accent) and the own accent ("") - the same goes for consonants such as "ç", which is broken down into two characters: the letter "c" and a cedilla.
Therefore, it would be enough to normalize the string and take the first character (because the normalization in NFD ensures that the letter without accent is always placed before the accent). For this, we use the module unicodedata
:
from unicodedata import normalize
def vogal_ou_consoante(letra):
if len(letra) == 0:
raise ValueError('string vazia')
letra = normalize('NFD', letra)[0].lower()
if letra in ('a', 'e', 'i', 'o', 'u'):
return True
elif letra in ('b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'):
return False
else:
raise ValueError('Não é vogal nem consoante')
print(vogal_ou_consoante('á')) # True
print(vogal_ou_consoante('Ç')) # False
print(vogal_ou_consoante('')) # ValueError
I included an extra check for the case where the string is empty (because there is no point normalizing and picking the first character), and in this case also throws an exception, since an empty string is neither vowel nor consonant.
There is only one detail: this implementation is kind of "naive", because as it always takes the first character, then it returns True
if the string starts with an accented character (e.g., for the string 'ábaco'
, the above function returns True
).
To correct this, we can check whether the second character onwards only has accents. For this we use combining
, that checks if the character is a Combining (category in which accents fit):
from unicodedata import normalize, combining
def vogal_ou_consoante(letra):
if len(letra) == 0:
raise ValueError('string vazia')
letra = normalize('NFD', letra)
# se do segundo caractere em diante tiver algum que não é acento
if not all(combining(c) for c in letra[1:]):
raise ValueError('string tem mais de um caractere')
letra = letra[0].lower()
if letra in ('a', 'e', 'i', 'o', 'u'):
return True
elif letra in ('b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'):
return False
else:
raise ValueError('Não é vogal nem consoante')
print(vogal_ou_consoante('á')) # True
print(vogal_ou_consoante('ábaco')) # ValueError
I use all
to check that all characters after the second (letra[1:]
) sane Combining. If any is not, launch the ValueError
.
Of course, that doesn’t mean that the another answer is wrong. She only considered the simplest case - and given the description of the question, it was unclear whether she needed to be as strict about the criteria as I did above. Anyway, here are the options.
And
vogal('á')
, with the accented, should return what?– Woss