Rodrigo πŸπŸš€ Profile picture
Nov 5, 2021 β€’ 20 tweets β€’ 7 min read β€’ Read on X
I gave you a Python 🐍 challenge πŸ†...

You delivered πŸ’ͺ!

This thread 🧡 will review some of your `argmax` implementations.

I'll also tell you what's good βœ… and bad ❌ about them.

I'll also tell you which one I think is the best, most Pythonic ✨ one.

Let's go πŸš€
The `argmax` function returns the index of the maximum element of the argument.

So, why don't we write this solution?

Compute the maximum, and then use `.index` to get its index! πŸ‘‡

But there are two issues here...

Can you see them? πŸ‘€
πŸ‘‰ first issue is that it traverses the argument twice;
(once to get the max, one more time to find its index)

πŸ‘‰ second issue is that it assumes the argument has a method `.index`, which might not be the case.

However, let's try and fix the first problem first...
If we only want to go over the argument once, then we can use a `for` loop to keep track of the maximum and its index!

Here's how you could write it πŸ‘‡

This goes over the argument only once βœ…

But if you've been paying attention to my tweets, you can improve it right away:
Instead of using `range(len(...))` which is almost always an anti-pattern ❌...

We can use `enumerate` πŸ‘‡

This cleans up the code a little bit βœ…

And it has another interesting effect...
By using `enumerate`, we no longer need to index into the argument to fetch the elements βœ…

Why is this good?

Because it means we can use MORE types of iterables as input.

But for this to be true, we need to get rid of the initial indexing in the first line πŸ‘‡
By using `float("-inf")`, we initialise the `mx` variable to something that will always be smaller than whatever is in the argument.

This means that the first iteration of the loop will *always* update the `mx` and `idx` variables.
Now, there's another fundamental difference between this solution and the previous ones.

The previous ones threw an error when the argument was empty.

This one returns -1 πŸ‘‡

Empty sequences do NOT have an argmax, so we want our function to raise an error when that happens.
How can we do this..?

Remember when we initialised `mx` with `lst[0]`?

We can do a β€œsimilar” thing, but that works on all types of iterables!

We just need to use `next` πŸ‘‡
By using `next` on the iterator over the argument, we get a `StopIteration` error if the argument is empty.

Now, callers of the `argmax` function can know if it failed or not πŸ‘‡
We are at a pretty good point, by now.

We have a function that works well, for a variety of input types.

Can we make it better..?

I say we can!

Let's talk about dictionary ordering 😁

Not `dict`.

Dictionary, as in, the book with the words!
Tuples are ordered like dictionaries; did you know that?

If you have a pair `(fst, snd)`, then the tuple is ordered by the value in `fst`.

If it matches the first element of the other pair, then the second element is used as a tie-breaker πŸ‘‡
Now, this is a technique I have seen often:

Let's put both things together:

πŸ‘‰ we take the values that define the ordering (the values of the argument);

πŸ‘‰ we take the values that we want to return (the indices);

and we zip them together.

Then, we call `max` 😊 πŸ‘‡
Again, the problem here is that we are using `range(len(...))`, which is not ideal ❌

Why?

Because not all iterables have a length.

Sadly, `enumerate` builds the tuples in the wrong order (first indices, then elements)...

But we could flip them! πŸ‘‡
But now...

We have the *initial problem* again ❌

We are going over the argument twice!

Once to build the enumeration with the correct ordering.

And another time to find the maximum!

Thankfully, we can fix this 😊

How?
Easy!

The `max` function accepts an optional argument called `key`.

We can use the `key` argument to change what/how the elements are compared within max!

For example, if `key` is the `neg` operator (the operator that negates numbers) we can use `max` to compute the `min` πŸ‘‡
What we have to do is the following:

Use the regular enumeration we get.

But use `key` to tell `max` to look at the values (the second element of the tuple) instead of the indices!

πŸ‘‡
We can do this with a little `lambda` πŸ‘‡

Beautiful πŸ”₯

But...

Let's make it just a little bit more pretty!

The `operator` module has an operator that renders our lambda useless!

Also, if we are not using `mx`, no need to assign to it, we can just use a sink variable.
In the end, this is what we get πŸ‘‡

What a beautiful solution:

βœ… errors on empty sequences
βœ… traverses argument only once
βœ… makes good use of built-ins
βœ… leverages the `key` argument from `max`
βœ… accepts a wide range of argument types

Do you like this solution?

I do 😁
Thank you to everyone who participated!

For reference, here is the original challenge, where you will find people who proposed really great solutions πŸ‘‡

Do you enjoy these challenges?

Because I do πŸ˜‚

Follow @mathsppblog for more content like this!

β€’ β€’ β€’

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

May 18, 2023
I know `print` is the first Python 🐍 function you learned! πŸš€

And yet, you don't know this about `print` πŸ‘‡ Image
What you know for sure is that `print` will take an object and it will print it on the screen.

That's the basic functionality it provides: Image
Maybe you don't know that `print` can actually print multiple things!

If you pass multiple arguments, they all get printed: Image
Read 11 tweets
May 17, 2023
I'll tell you the story of a person that had the wrong name…

And how to prevent that in Python 🐍 with properties πŸš€.

πŸ‘‡ Image
John Doe was a regular guy and when he was born, he was inserted into the government's database of people.

They created a new `Person` and added John's details: Image
John never liked his name Doe, though.

So Joe decided to change his name to Smith.

And so he did.

He updated his last name, but the government `Person` STILL had the wrong name! Image
Read 10 tweets
May 14, 2023
Opening a file to read/write is a common task in Python 🐍.

Here is how to do it right! πŸš€

πŸ‘‡ Image
Python has a built-in `open` that takes a file path and opens that file.

Then, you have to specify whether you want to open the file to read, write, or append.

But this isn't half of the story! Image
The default behaviour is to open the file to read/write text.

This works well with TXT or CSV files, for instance.

If you need to open a file to read its binary contents, you can add a `"b"` to the mode: Image
Read 6 tweets
May 13, 2023
The Python 🐍 built-in `round` is great. πŸš€

Here are some tips on it. πŸ‘‡ Image
The purpose of `round` is to… round numbers!

It rounds numbers to the closest integer.

These are some simple examples: Image
However, if the number ends in `.5`, what is the closest integer?

In that case, `round` will choose the even number.

This means it may round up or down πŸ€ͺ

(In school, I was taught to round `.5` up… 🀷) Image
Read 6 tweets
May 12, 2023
Error handling in Python 🐍 made simple. πŸš€

πŸ‘‡ Image
The keyword `try` is used before code that might fail.

So, if you know something can raise an error, you can write it inside a `try` statement: Image
Now that the code is inside a `try` statement, you need to tell Python what error you want to handle, and how.

That's when the keyword `except` comes in! Image
Read 7 tweets
May 11, 2023
Every Python 🐍 programmer uses the ICPO rule. πŸš€

Even if you don't know what it is.

Let me tell you what the ICPO rule is and what it does πŸ‘‡ Image
The ICPO rule is what determines how attribute and method lookup work.

More precisely, it is the ICPO rule that tells Python where to look for attributes and methods in your objects.

This is relevant because everything in Python is an object.
Because everything is an object, that means Python is incredibly consistent in some things.

For example, did you know that everything has a type? Image
Read 13 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

Don't want to be a Premium member but still want to support us?

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

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(