Home Artificial Intelligence Don’t Forget That Python Is Dynamic!

Don’t Forget That Python Is Dynamic!

0
Don’t Forget That Python Is Dynamic!

This whole situation begs the next query: Can we indeed need all those type hints, static type checkers, and runtime type checkers?

I’m not going to answer this query. This is principally because I’m removed from being one among those individuals who think they know every little thing about… well, about every little thing, or almost every little thing. But I hope to ask you to take into consideration this yourself.

I’ll, nevertheless, remind you — and myself — that Python’s dynamic typing, also called duck typing, lies behind the success of this language. Below is the favored explanation of how duck typing works:

If it walks like a duck and it quacks like a duck, then it should be a duck.

Duck typing could be very powerful without type hints and runtime type checking. I’ll show you this on quite simple examples, and without further ado, let’s jump into this straightforward example:

Catching errors by runtime type checking, and customizing message
Catching errors by runtime type checking. Image by writer

Here, we’re checking forms of x and y, and each ought to be strings (str). Note that this manner, we’re type of checking whether what we offer to the str.join() method is tuple[str, str]. Definitely, we don’t have to ascertain if this method gets a tuple, since we’re creating it ourselves; enough to ascertain the forms of x and y. When either of them isn’t a string, the function will raise TypeError with an easy message: "Provide a string!".

Great, isn’t it? We’re secure that the function might be run only on values of correct types. If not, we are going to see a customized message. We could also use a custom error:

Now, let’s remove the sort check and see how the function works:

Catching errors without runtime type checking; the message is not custom by built-in
Catching errors without runtime type checking; the message isn’t custom by built-in. Image by writer

Ha. It appears to be working in quite an identical way… I mean, an exception is raised principally in the identical place, so we’re not risking anything. So…

Indeed, here the function foo_no_check() uses duck typing, which uses an idea of implicit types. On this very example, the str.join() method assumes it takes a tuple of strings, so each x and y need to be strings, and in the event that they aren’t, the implicit type for tuple[str, str] has not been implemented. Hence the error.

You would say: “But hey! Have a look at the message! Before, we could use a custom message, and now we will’t!”

Can’t we indeed? Look:

Catching errors without runtime type checking. The error message is customized via a try-except block.
Catching errors without runtime type checking; the error message is customized. Image by writer

We will now see each messages: the built-in (sequence item 1: expected str instance, in found) and custom (Provide string!).

You would ask: What’s the difference? So, I check types. What’s the issue?

Well, there is quite a difference: performance. Let’s benchmark the three versions of the function, using the perftester package:

Listed below are the benchmarks:

perftester benchmarks
Benchmarks performed using the perftester package. Image by writer

For all benchmarks in this text, I used Python 3.11 on a Windows 10 machine, in WSL 1, 32GM of RAM and 4 physical (eight logical) cores.

Within the second line, I set the default variety of experiments to 10, and inside each run, each function it to be run 100 million times. We take the perfect out of the ten runs, and report the mean time in seconds.

The foo() function, so the one with the runtime type checks, is significantly slower than the opposite two. The foo_no_check() function is the fastest, although foo_no_check_tryexcept() is just a little bit slower.

Conclusion? Runtime type checks are expensive.

You would say: “What? Are you kidding me? Expensive? It’s only a minor a part of a second! Not even a microsecond!”

Indeed. It’s not much. But it is a quite simple function with only two checks. Now imagine an enormous code base, with many classes, methods and functions — and a looooot of runtime type checks. Sometimes, this may occasionally mean a big decrease in performance.

Runtime type checks are expensive.

When reading about duck typing, you’ll normally see examples with cats that meow, and dogs that don’t, and cows that moo. While you hear an animal meowing, it’s neither a dog nor a cow, it’s a cat. But not a tiger. I made a decision to make use of an atypical example, and I hope it was clear enough so that you can see the strengths of duck typing.

As you see, Python exception handling does an ideal job in runtime type checking. You will help it by adding additional type checks when needed, but all the time keep in mind that they may add some overhead time.

Conclusion? Python has great exception-handling tools that work quite well. Oftentimes, we don’t have to make use of runtime type checking in any respect. Sometimes, nevertheless, we may have to. When two types have similar interfaces, duck typing can fail.

As an example, imagine you desire to add two numbers (x + y), however the user provided two strings. This may not mean an error because you possibly can add two strings. In such instances, chances are you’ll have to add a runtime type check, in case you don’t want this system to proceed with these incorrect values. Eventually it could possibly break anyway, so the query is whether or not you would like this system to proceed until then or not. If yes, chances are you’ll risk an exception might be raised much later, so adding a sort check can actually help save time. As well as, raising the exception later in this system flow can mean difficulties find a real reason behind the error.

All in all, I are not looking for to let you know that runtime type checking should never be used. But oftentimes, such checks are added after they aren’t needed.

LEAVE A REPLY

Please enter your comment!
Please enter your name here