Checking if a string is a pangram

This checker works perfectly but I have a strong feeling that I could make this script much more compliant with the "Zen of Python", especially having read a book about it recently. There is no doubt that I made this script much longer than needed, so if you see something I can shorten, please say so.

#!/usr/bin/env python

myPhrase = "The quick brown fox jumps over the lazy dog"

def is_pangram(phrase):
c = 0
alphabet = "abcdefghijklmnopqrstuvwxyz"
phraseLetters = ""
for char in phrase:
for letter in alphabet:
if char == letter and char not in phraseLetters:
phraseLetters += char
for char in phraseLetters:
for letter in alphabet:
if char == letter:
c += 1
if c == 26:
return True
else:
print phraseLetters, alphabet
return False

print is_pangram(myPhrase)


This can be improved and made more Pythonic. First, there is the in and not in keyword, which you already know of as you used it. Why not utilize that instead of doing two loops?

You will have to change the logic slightly:

def is_pangram(phrase):
alphabet = "abcdefghijklmnopqrstuvwxyz"
phraseLetters = ""
for char in phrase:
if char in alphabet:
phraseLetters += char
for char in alphabet:
if char not in phrase:
print phraseLetters, alphabet
return False

return True


I iterate over the letters in phrase, checking if they are in the alphabet, and add them to the phraseLetters string. Then, I iterate over the alphabet and check whether each character is in the phrase.

This can be simplified even further if you are not interested in printing the letters that are in the phrase. I would recommend cutting it down even further to this:

def is_pangram(phrase):
alphabet = "abcdefghijklmnopqrstuvwxyz"
for char in alphabet:
if char not in phrase:
return False

return True


This way, you can check whether the phrase is a pangram without always printing to the screen, which is good for re-using the code later; you can then write another method to print the strings in the phrase and the alphabet. Additionally, I would recommend printing the letters in phrase alphabetically so it is easy to see which letter is not in phrase, which will also make it easier for any potential debugging you have to do.

Edit:

Thanks to matsjoyce in the comments, you can simplify it even further to this:

def is_pangram(phrase):
alphabet = "abcdefghijklmnopqrstuvwxyz"
return not (set(alphabet) - set(phrase))


This way, you can even return the letters not in phrase for ease of reference by just removing the not keyword, but you would also have to change the name of the method as is_something implies a boolean value being returned.

Additionally, you should probably name alphabet in all caps to signify it is a constant variable.

• Thanks for these edits. I was not interested in printing out the results in the first place, that was just the way I was debugging my code when it wasn't working earlier. Again, thank you for for the help. – Scoutdrago3 Mar 14 '15 at 1:11
• You probably can simplify it further by using the all function by writing the python equivalent of alphabet.All(phrase.Contains) – CodesInChaos Mar 14 '15 at 10:00
• @CodesInChaos That would be all(x in phrase for x in alphabet) – Random832 Mar 14 '15 at 12:23
• Or you could do not (set(alphabet) - set(phrase))... – matsjoyce Mar 14 '15 at 15:23
• Or set(alphabet) != set(phrase)!? – mkrieger1 Jan 9 '17 at 18:04

We want to check that the phrase contains all letters of the alphabet. Such wording more or less suggests your code organization.

Lets rewrite the goal to an equivalent: check that each letter of the alphabet is present in the phrase. Or, even simpler, that no letter of the alphabet is missing. Such wording immediately leads to much simpler approach:

for letter in alphabet:
if not letter in phrase:
return False

return True


I'd let the set class do all the work.

english_alphabet = frozenset('abcdefghijklmnopqrstuvwxyz')

def is_pangram(phrase, alphabet=english_alphabet):
if not isinstance(alphabet, (set, frozenset)): alphabet = set(alphabet)
return alphabet <= set(x.lower())


The use of a set also naturally allows you to print what letters are not in the phrase

>>> phrase="The quick red fox jumps over the lazy dog"
>>> sorted(english_alphabet - set(phrase.lower()))
['b', 'n', 'w']

• Thanks for the response. Never thought of using sets. I may need to read up on these, as I known very little about them. – Scoutdrago3 Mar 14 '15 at 15:29
1. Whenever you need to perform calculations that involve the alphabet I'd recommend initializing your alphabet variable in the following way:

import string
a_to_z = string.lowercase
a_to_m = string.lowercase[0:13]
# etc

2. Notice that if we remove the second occurrence of the letter t from myPhrase than your is_pangram returns false:

is_pangram('The quick brown fox jumps over the lazy dog') # True
is_pangram('The quick brown fox jumps over he lazy dog') # False


To remedy this bug just call phrase.lower().

3. A possible concise (and correct) solution to your problem using the built-in methods for sets in Python:

import string

def is_pangram(phrase):
return set(string.lowercase).issubset(set(phrase.lower()))


Here is my proposal:

import string
def meet_pattern(s):
s = list(s)
pattern = string.ascii_lowercase + ' '
return all(pattern[i] in s for i in range(1, len(pattern)))

s = input().strip()
letters = set(s.lower())
if meet_pattern(letters):
print("pangram")
else:
print("not pangram")


It is certainly not the shortest way to write this code but it is readable. Also noticed that I am using the string module that way I can simply switch to different string types :

>>> help(string) # on Python 3
....
DATA
ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
digits = '0123456789'
hexdigits = '0123456789abcdefABCDEF'
octdigits = '01234567'
printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_{|}~ \t\n\r\x0b\x0c' punctuation = '!"#$%&\'()*+,-./:;<=>?@[\\]^_{|}~'
whitespace = ' \t\n\r\x0b\x0c'