Python follows the EAFP (Easier to Ask Forgiveness than Permission) rather than the LBYL (Look Before You Leap) philosophy. The Python philosophy of EAFP is somewhat linked to its "duck typing" style of coding.
When a programmer creates data in code, whether it's a constant or a variable, some programming languages need to know what "type" of data it is. For example, if you set a variable to 13, a computer doesn't know whether you mean for it to be used as a word ("thirteen") or as an integer (as in 13+12=25 or 13-1=12). This is why many languages require programmers to declare data before it's used.
For example, in this C++ code, the mynumber variable is an integer type and the myword variable is a string:
#include <iostream>
#include <string>
using namespace std;
int mynumber = 13;
string myword = "13";
int main() {
std::cout << 13+2 << endl;
std::cout << "My favourite number is " + myword << endl;
}
Python is clever, though, and it uses the "duck test": if a variable walks like a duck and talks like a duck, then it is a duck. In other words, Applied to computer science, that means Python examines data to determine its type. Python knows that integers are used for math and words are used in communication, so the programmer doesn't have to explain to Python how to use the data it finds in variables. Python uses duck typing to figure it out on its own, and does not attempt to do math on strings or print the contents of arrays (without iteration), and so on.
However, before we discuss these concepts, let's go over the basics:
An analogy to understand the concept of "type"
The concept of "typing" in the context of a programming language is often discussed, but often, the deeper meaning eludes us. So, let me try to explain the concept using an analogy.
In a computer program, objects and other items are stored in memory, and they are generally referred to by some "variable name." So, when you create an object of a particular class (in any of the popular programming languages), you are basically reserving a portion of memory for that object to occupy, and then you refer to this object with that variable name.
So, as an analogy, you can think of this space in memory as a kind of container or box. For this exercise, let's call it a box. So now we have two things with us—an object and a box which contains it.
To take the argument further, typically, a box must be "designed" to be able to hold the object that it contains (i.e., a box meant for holding matches won't be ideal for holding shoes, or vice versa, even though it's physically possible). So, we can agree that the object and the box which contains it must both be of a similar type?
This, in fact, is the so-called "static typing." Basically, it means that not only the object must have a "type," but the variable name (a.k.a. the box) must have one as well, and it should be the same or similar. (I will explain why I say "similar" in a moment). This is why, in statically typed languages like Java/ C++, you need to define the type of the variable when you create it. In fact, you can create a variable name analogous to a box, even without creating any object to put in it. You can't do this in Python.
However, a dynamically typed language like Python works differently. Here you can think of the variable name, not like a box but rather analogous to a "tag" (somewhat like a price tag in a store). So, the tag does not have a type. Rather, if you ask the tag what its type is, it would probably pick the object it is tagged to at that moment. Why I say "at that moment" is because, just like in the real world, a tag attached to a shoe could also be attached to some other item at a different time. So the Python interpreter does not assign any type to a variable name, per se. But if you ask a variable name its type, then it will give you the type of the object it is currently tied to. This is dynamic typing.
This dynamic vs. static typing has a direct impact on the way you write code. Just like in the real world where you can't put shoes in a box meant for matches, so it also goes in statically typed languages—you generally cannot put objects of one type in a variable name created for objects of another type.
Strongly typed vs. weakly typed languages
There is another important concept to address here, namely, strongly and weakly typed languages. The "strength" of typing has virtually nothing to do with whether it is dynamically or statically typed. It has more to do with "casting" or the ability to convert one type of object to another. Contrary to popular perception, Python is a rather strongly typed language, just like C++ or Java. So in Python, for example, you can't add, say, an "integer" to a "string," however, you can do this in a language like JavaScript. JavaScript, in fact, is one of the notoriously "weakly typed" languages. So, it should be clear that strong/weak typing is an altogether different scale than static/dynamic typing. In general, scripted languages like Python tend to be dynamically typed, while compiled languages tend to be statically typed.
Duck typing and EAFP and LBYL
Python follows the duck typing style of coding.
Let's again take a real-world example. Suppose you have an object "Machine M." Now, you don't know whether this Machine M has the capability to fly or not. The way LBYL would proceed vs. EAFP is illustrated in the figure below:
Let's clarify the concept with some Python code (with fictitious functions):
# LBYL:- Look Before You Leap
if can_fly():
fly()
else:
do_something_else()
# EAFP:- Easier to Ask Forgiveness than permission
try:
fly()
except:
clean_up()
How duck typing supports EAFP
Duck typing is ideal for the EAFP style of coding. This is because we don't care about the "type" of an object; we care only about its "behavior" and "capability." By "behavior," I basically mean its attributes, and by "capability," I mean its methods.
To sum up:
If you see a lot of if-else
blocks, then you are an LBYL coder.
But if you see a lot of try-except
blocks, you are probably an EAFP coder.
9 Comments