πŸ₯‡ I challenged you to write a Python 🐍 function `is_pangram`, that checks if a string contains all 26 letters of the English alphabet.

(Original challenge linked below πŸ‘‡)

I got many amazing suggestions.

Here is a thread 🧡 about different solutions.

Overall, people tended to follow one of 3 approaches:

1. remove irrelevant chars and see if there are 26 letters left (duplicates excluded);

2. check if each letter of the alphabet appears in the string; and

3. check if the alphabet is fully contained inside the input string.
Then, I saw two major trends in the solutions people shared, regardless of strategy.

Either they did all the work by hand with a `for` loop or they leveraged tools like Python's `set`.

Here's the archetype for a `for` based solution: Image
Notice the usage of the `string.ascii_lowercase` built-in that contains the lower case latin alphabet.

This is a built-in, so make use of it!

Might look silly, but you won't believe the amount of incorrect alphabets found on GitHub πŸ€¦β€β™‚οΈπŸ€¦β€β™€οΈ

Personally, I am not very fond of the β€œremoving what doesn't matter and checking if there's 26 letters left” strategy.

But WHY?

It is very error prone! If you forget to exclude one single type of character, then your solution πŸ’₯πŸ’₯

But if you really wanna go down that road...
Then you can do something like the solution below πŸ‘‡

However, this solution will be hard(er) to patch up if you want to take into account accented letters like ãéô, arbitrary Unicode characters, etc. Image
Now, there's a slight semantic difference, but instead of "removing what doesn't matter" you can choose to "keep what matters" with a set comprehension!

With this shift in mindset we can use something like `str.isalpha` to only keep "letter" characters: Image
Another good archetype of solution is to go through the letters that we want to find and see if they are inside the input string or not.

This solution is easy to understand and IMO superior to the raw `for` loop because it makes idiomatic use of the `all` built-in... Image
But this solution is not optimal!

If you have a really long string and the letters of the alphabet are towards the end of the input, then the usage of `in` will do a lot of work...

Think about strings starting with
`"_" * 1_000_000`,
you have to skip a million irrelevant "_".
So it really is helpful if we only traverse the input string once, so let's stick to solutions using sets.

There are many ways to phrase the solution using many equivalent formulations using set operations.

Here is an example: Image
However, my favourite solution, the one that I find most elegant, just checks if the set of lowercase letters is a subset of the set of all characters of the string!

This entails minimal preparation, it's just a single check: Image
The <= inequality is akin to the βŠ† in mathematical notation; if A and B are Python sets,

A <= B

checks if A is a subset of B.
This is the same as A.issubset(B).

You can read up on set operations here: docs.python.org/3/library/stdt…
This type of solution also makes it very easy to make our solution much more robust, e.g. handling accents in letters.

For example, let's say that the following should also be a pangram:

Γ bΓ§dΓͺfghΓ­jklmΓ±Γ΅pqrstΓΌvwxyz

Using `unicodedata.normalize` we can handle that! Image
I can't guarantee that the normalisation step above is the ABSOLUTE BEST, especially because I'm not a master of Unicode, but you can read always read the docs.

The normalisation above splits letters and accents:

Γ Γͺíñáü β†’ a`e^iΒ΄n~o~uΒ¨

πŸ”— docs.python.org/3/library/unic…
Finally, I'd just like to thank explicitly everyone that contributed to the discussion in the original challenge, submit solutions, etc.

If you like this type of educational content, retweet the beginning of the thread and follow me (@mathsppblog) for more πŸ˜‰
P.S. in hindsight, the normalisation call should have been first and only THEN I should've changed the casing of the string to lower case.

β€’ β€’ β€’

Missing some Tweet in this thread? You can try to force a refresh
γ€€

Keep Current with Rodrigo πŸπŸ“

Rodrigo πŸπŸ“ Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @mathsppblog

9 Sep
Have you ever had a hard time debugging 🐞 a Python 🐍 script?

If you say you haven't, you're lying! πŸ˜†

Let me show you something that you're gonna love me for!

Run your script with the `-i` flag!

By doing so, you get a REPL when the script finishes, EVEN with errors πŸ’£

1/4
Let me show you an example.

Below is a piece of code I wrote in a script called `foo`.

If you look closely, you'll see I'm generating 100 random integers...

All of them between 0 and... 0! So I'll generate 100 zeroes (my bug).

Later, a 0/0 division will throw an error.

2/4
When I run the code with the `-i` flag, the terminal shows the error and immediately opens a REPL!

From within the REPL I can inspect my variables and figure out what went wrong!

Isn't this amazing ⁉

3/4
Read 5 tweets
8 Sep
Have you ever tried to optimise your Python 🐍 code, but weren't able to tell which alternative implementation was faster?

Let me teach you how to use the `timeit.timeit` method to measure execution time of small code snippets.

A thread πŸ‘‡πŸ§΅
If you have a snippet of code you want to time, you can use the command line to measure its execution time.

For example, how long does it take for Python 🐍 to add up the first 10,000 integers?

Using the command `python -m timeit`, I get that it takes 189 microseconds.
The execution of `python -m timeit` reports some more info:

β€œ2000 loops, best of 5: ...”

I'll explain what that means, but first let me show you how to use `timeit` in your code.

(Oh, btw, there's a MUCH better way to add consecutive integers! πŸ‘‡)
Read 23 tweets
8 Sep
Did you know that Python 🐍 supports keyword argument unpacking?

What's that?

If you have a function that accepts a series of arguments: `arg1`, `arg2`, ...

And if you have a dictionary with keys named `arg1`, `arg2`, ...

Then you can use the dict's values as arguments: Image
The `**` in front of `kwargs` does the unpacking in the function call.

`my_func(kwargs)` – this calls `my_func` with a single argument, the `kwargs` variable (a dictionary in the image above)

...
`my_func(**kwargs)` – this calls `my_func` with one named argument for each (key, value) pair in the dictionary.

E.g., in the above,

`my_func(**kwargs)` unpacks to `my_func(a=73, b="hey!", c=(1, 10))`.
Read 4 tweets
7 Sep
@svpino Hey Santiago! I'd just like to clarify something for the readers of this thread:

The formula

P(A or B or C) = P(A) + P(B) + P(C) - P(A and B and C) is NOT β€Ό true in general.

The result works here because the events are mutually exclusive and P(A and B and C) = 0

1/
@svpino But the actual general formula is

P(A or B or C) = P(A) + P(B) + P(C) - P(A and B) - P(B and C) - P(A and C) + P(A and B and C)

You can read up on the inclusion-exclusion principle, which explains this: πŸ”— en.wikipedia.org/wiki/Inclusion…

This can also be explained visually:

2/
@svpino Checking the overlaps below πŸ‘‡

The ellipsis (representing the events) can overlap all over the place, and we CANNOT overcount the overlaps!

So, we need to remove the pairwise overlaps:

A and B
A and C
B and C

But when we do that, we over-subtract the final intersection,

3/
Read 4 tweets
2 Sep
Have you ever seen anything like this in Python 🐍 loops??

I just saw this being used for the first time, ever, and I decided to figure out what the heck this was...

And so here goes the explanation πŸ‘‡πŸ§΅

1/
Foreword:

Python has many amazing capabilities to deal with `for` loops, among which you can count the `zip` and `enumerate` built-ins.

This little thread assumes you are familiar with both.

Read these if you need:
πŸ”—Β mathspp.com/blog/pydonts/e…
πŸ”—Β mathspp.com/blog/pydonts/z…

2/
When using `zip` and `enumerate` in your `for` loops, something you do often is to unpack the values you are iterating over.

Unpacking, in and out of itself, is an amazing feature of Python, and you can read about it in the linked article if needed.

mathspp.com/blog/pydonts/d…

3/
Read 10 tweets
24 Aug
Did you know that Python 🐍 has over 240 modules in the standard library?

Here is a MEGA thread 🧡with a super high level overview of those modules...

... but before that, I challenge you to name as many modules by heart as possible! πŸ’¬πŸ‘‡

Let's see who gets more!
To help get a sense for the more popular and used modules, leave a like β™₯ on the tweets that contain modules you remember using.

Can you guess what module(s) are the most widely used..? πŸ€”

(P.S. To learn more about all these cool modules, check docs.python.org/3/library/)
Let's kick things off with

πŸ‘‰ Text Processing Services
πŸ”— docs.python.org/3/library/text…

1️⃣ string β€” Common string operations

2️⃣ re β€” Regular expression operations

3️⃣ difflib β€” Helpers for computing deltas

4️⃣ textwrap β€” Text wrapping and filling
Read 89 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!

:(