1

I have a lot of repetition in my code, a prime example is when I'm doing a simple check to see if the first letter of a string is a vowel or not. The code I have is as follows :

if word[0] == 'a' or word[0] == 'e' or word[0] == 'i' or word[0] == 'o' or word[0] == 'u':
    print 'An', word
else:
    print 'A', word

This works fine but the amount of repetition leads me to think there could be an easy way to shorten this, I just don't know of it. I also tried this code:

if word[0] == 'a' or 'e' or 'i' or 'o' or 'u':
    print 'An', word
else:
    print 'A', word

However, this code returned True for every word, regardless of beginning letter.

So, just to clarify. The code works fine and it fully functional and I know I could define it as a function and just use that but it seems like it could easily be shortened and this knowledge would be useful on multiple projects.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343

3 Answers3

3

Test for membership using in:

if word[0] in {"a","e","i","o","u"}

Also if word[0] == 'a' or 'e' or 'i' or 'o' or 'u' would always evaluate to True, you are basically checking if word[0] == "a" then if bool("e") which will always be True for any non empty string.

Not a big deal for a small test like you are doing but set lookups are 0(1) as opposed to 0(n) for a list, string etc. so a much more efficient solution when dealing with larger data or many repeated lookups.

You can also pass a tuple or letters to str.startswith:

if word[0].startswith(("a","e","i","o","u")):

If you want to ignore case, call word[0].lower() on the letter.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
2

Test it using the keyword in.

word = "hello"
vowels = frozenset("aeiou")

if word[0] in vowels:
    print "It's in!"
else:
    print "It's not."

Note that you can have your vowels in anything iterable, set, list, string, dict, a generator function or whatever you like.
As pointed out by @MartijnPieters in the comments, the frozenset is the most optimised way to do this.

d6bels
  • 1,432
  • 2
  • 18
  • 30
  • 1
    By making `vowels` a variable you miss out on a CPython optimisation that will look up the set as a constant (stored as a `frozenset()` object with the code object) from Python 3.2 onwards. Using `if word[0] in {'a', 'e', 'i', 'o', 'u', 'y'}` would be more efficient because of that optimisation. Here the difference will be minuscule however. – Martijn Pieters Mar 06 '15 at 11:24
  • @AvinashRaj because it is one in French and I didn't pay attention :-) , MartijnPieters well like you said it's minimal but I'm glad to learn about those. Still a variable is greatly flexible! – d6bels Mar 06 '15 at 11:48
  • @MartijnPieters I think I'll make that a frozenset then ;) – d6bels Mar 06 '15 at 12:11
  • 1
    Much better; now it matches what Python'll do, and if this is inside a function then the performance will match almost exactly. The only difference is that Python creates the optimised constant just once (when loading the code object from the `.pyc` cache), while your code creates the object each time the function is called. – Martijn Pieters Mar 06 '15 at 12:21
1

You could try with re module.

if re.match(r'(?i)[aeiou]$', word[0]):

This would handle both upper and lower case vowels. (?i) called case-insensitive modifier which helps to do a case-insensitive match. Since match function tries to match the string from the begining, you dont need to add the starting anchor ^. [aeiou] character class which matches a or e or i or o or u.

Avinash Raj
  • 172,303
  • 28
  • 230
  • 274