NOTE: Download Levinux (~15MB) and run Python, vim and git on Linux Server with from your Mac, Windows or Linux desktop with a double-click (no install) in just a few minutes! Fall in love too.
This article is not about Python vs. other languages, so much as it is about those peculiarities of Python that made it endearing to me—a non-professional programmer whose achievements at work came directly from being able to program. I code almost every day at my job… but not as my job. If that sounds anything like you, then Python may be for you. And yes, ultimately I do believe it’s better than getting started with JavaScript—though I really do appreciate and struggle with the counter-argument. In the end, it comes down to the fact that I really just love Python. Read on to learn why…
Just a few words on my background
My first programming experience was in 1982 on BASIC on a Radio Shack TRS-80 in summer camp, and I worked for Commodore as a student intern from ’88 through ’90. Yet, I never became a professional programmer, instead taking it up whenever I felt it needed—but never explicitly as a stated part of my job. I’ve carried out sizable projects in AREXX, TCL and VBScript. At various times, I’ve attempted to take up PERL, C++, Java, ASP.NET and Ruby. Of course, I dabble in JavaScript, and intellectually, I’m drawn to LISP—keenly aware that I am still blinded by the Blub Paradox.
But today, it’s time to talk about Python’s strengths, and why for the first time in my life, I’ve found a computer programming language I actually love—so, this may be a good read if you’re choosing your first programming language. Most of the other languages I mentioned ended up feeling like a necessary evil in getting my job done. In all fairness, a couple years ago I thought I loved Ruby, but in hindsight, it may have just been an infatuation. Although in many ways, Python and Ruby are comparable, Ruby just didn’t stick, and today I attribute that to its forced objected oriented-ness, which didn’t always feel necessary and subtly dissuaded me from Ruby.
Ruby and Python have a good amount of similarity, and they’re both sometimes described as “fitting your head.” In other languages, the portion that doesn’t fit in your head is… drumroll please… overhead! The rest of this article is dedicated to the advantages of Python programming that actually got me excited about coding again.
Cutting back the overhead – Python indentation advantages
Now what do I mean by this? The clearest example is the non-use of curly braces—or brackets of any sort, for that matter—as code block delimiters. One of the most debated matters of programming style in BCPL-derivative languages such as C and Java is “indent styles”, or how to arrange those curly-brackets for maximum readability—which happens to make absolutely no difference in how your program actually runs.
Ruby, Visual Basic and most non-C derivative languages use keywords such as Ruby’s “def” and “end” for code blocks, instead of using curly-brackets. Python takes it one step further and uses a keyword only at the beginning. The rest of the code block is contained merely by virtue of line indenting.
For example, Python vs other languages…
C++ (Placement of curly brackets arbitrary and a matter of style):
void function functionname(arg)
{
some code
}
…which can also be stated in this and other ways…
void function functionname(arg) {
some code
}
You can’t imagine the time wasted on the discussion of the merits of each, and how ugly code gets when followers from each camp try to combine their code. Keyword languages are a little different…
Ruby (Uses keyewords instead of curly braces)
def functionname(variable)
some code
end
This is still allows matching end confusion to occur, especially with nested functions…
def functionname(variable)
def nestme
more code
end
some code
end
Python (Only one keyword, but indents matter)
def functionname():
some code
There is only one way to state this in Python. This is profound. Take a moment to let that seep in. It is both Python’s greatest strength and weakness. It is either the reason you will embrace it and love it for the rest of your life, or will run away, trying not to pollute your mind with anything that doesn’t follow the pervasive C / C++ / Java / JavaScript / PHP, etc, etc, curly-brace model.
You can indent the first line as much or as little as you want, but then all subsequent code in the block must remain consistent with the first line. By convention, most people use 4 spaces for an indent (not tabs), but I use only 2. I’m a rebel. That’s about as rebellious as you can get with Python programming style. Now this flies in the face of modern programming conventions where white-space generally doesn’t matter, and code formatting is largely a matter of personal style. But Python uses the equivalent of a school dress-code, ensuring a certain amount of conformity between members, and… drumroll please… the elimination of overhead!
With enforced formatting, not only do you not need to decide how to dress in the morning with Python, but you also purge hundreds of lost man-hours resulting from indenting ambiguity. If it’s indented in Python, then it belongs to that code-block. Readability skyrockets and misunderstandings plummet (apologies to personal expression). Not only is this a personal advantage in efficiency, but it makes Python well suited for open source or other projects where programmers must collaborate on the same code.
Speaking of terse code, every variable and object in Python also has a True-state or a False-state, IN ADDITION to whatever else they contain, so you can directly check whether they’ve been used yet. False returns on any number-variable set to Zero (0) or any string-variable set to Empty (”) or any instantiated object variable like a list, but which has no entries ([]). So you can instantiate variables or objects setting them to zero or empty, and check them later. Therefore, one of the most common if-checks that we do becomes ridiculously terse and readable:
if checkme: print "I have been used"
The flip-side is that very few other popular popular programming languages work this way, in which indenting matters, and everything is capable of returning True or False. If you are making Python your “native language” (i.e. your first programming language—the language that you will end up “thinking in”), you are NOT getting used to the standard conventions of the world’s most prevalent languages, that all litter themselves up with curly braces: Java, JavaScript and all variations of C, such as C#, C++ and Objective C.
Whether this is a good thing or not is a matter for philosophical debate. I made my choice, preferring to strip out the overhead in 99% of what I do day-to-day, and deal with it in those rare instances when I really have to use C to optimize my Python code (more on that later). After stints with Java, C++ and JavaScript, you can’t imagine the relief of getting curly braces out of my life.
No ambiguous ducks allowed!
Okay, so indenting matters, and that’s the single biggest personality-shaping characteristic of Python. Code gets more terse. Speaking of terse, Python doesn’t require declaring and typing variables before using. The first time they’re used, they’re declared, and they hang around until you go out of scope, and they get destroyed. This is called duck-typing with garbage collection—if it quacks like a duck, then it’s a duck.
This is the second most personality-defining part of the Python programming language from my perspective, but not just for duck typing, but for how it’s implemented to reduce misuse. It’s hotly debated whether duck typing is good for writing stable, bug-free code, but it’s awesome for making your program even shorter and even more readable. But how can a language be worthy of loving if ambiguous duck typing lets bugs in?
As a concession to keeping bugs out, Python creates a forced-stop error if you try to do anything ambiguous with a variable, like adding a string and an integer. For example, ‘Ni’+3 would fail, because Ni is a string and 3 is an integer, so you might want concatenation or type conversion. But ‘Ni’*3 will succeed, because there is no such ambiguity and this can only mean Ni 3 times:
NiNiNi
So, Python is indeed duck-typed, but it won’t do any of that auto-conversion magic for you if there’s more than one possible meaning. That gives you all the advantages of duck typing without the one whopping huge drawback (of letting bugs in). If it’s ambiguous, it raises a fatal error—that’s a pretty big tenant of Python. The net result is that all those bugs that variable-typing Nazis get furious about just don’t happen in practice because the code immediately stops running until you fix it. Again, the net result is shorter, more readable, reliable code that also happens to be easier and faster write.
But to really appreciate this, you have to consider the time you burn typing and sizing variables in strictly static languages, like C or… gulp… Scala. In those languages, you must do it on every new variable at the time it is created. This is basically everywhere in your program—hundreds, thousands, tens-of-thousands of times throughout your program. With explicit variable typing comes tons more code to manage, whereas with Python, you must be explicit only at the moment where something might be misinterpreted when you try to use it, otherwise the program won’t run.
These instances of ambiguity are less-common, and identified for you the moment you try to run the program, so Python is actually still favoring explicitness. It just favors it at the less-common moments when it’s necessary versus burning time with explicitness all over the place (for no good reason, given Python uses an interpreter versus a compiler). Of course static languages that force you to declare and size variables, and then use compilers to make the most efficient executable code possible to run faster, and there is advantage in that. But so long as you’re using an interpreted language (yes, I know… Python and its ilk are actually pseduo-compiled bytecode), you might as well get the full advantage of immaculately terse and readable duck typed code. This is one of those cases where the arguments for well-designed high-level interpreted languages will get stronger as CPU processors get faster, and you’re no longer paying to save YOUR time as a developer at the expense of your users. I think we’re already there.
By Ref unless otherwise Stated.copy()
What else do I love about Python? Well, it made me finally understand the by-reference versus by-value that I found so ambiguous in other languages. In Python, everything is automatically a reference, and you have to ask explicitly to get a copy of a truly new copy of an object in memory using methods such as .copy(). I find that I get just a better understanding of what’s going on, and that it becomes harder to create accidental memory-bloat. This applies to objects, and not to numbers, which takes awhile to keep clear in your head, but the pragmatic benefit is worth it. So, for example:
#This will make a copy of the integer >>> a = 1 >>> b = a >>> b = 2 >>> print a 1 #But this will reference the original object >>> a = [1] >>> b = a >>> b[0] = 2 >>> print a [2]
This is because the first case is an integer, and there’s little opportunity for memory-bloat or object-mangling, so the behavior that you’ve come to expect in every other language applies. You can instantiate new instances of numbers all over the place without limit, and they are essentially copies of each other. But try to do the same thing with any complex data object—a list in this case—and you will see everything suddenly start to become references to the original object in memory. Why is this the pragmatic choice for Python behavior? Why is this good?
It is more likely that you will mangle your original objects in-place, resulting in instantly noticeable bug, rather than ones that slip quietly by, growing in size in the background until your system performance slows down. In-short, Python has made you much less vulnerable to one of the the easiest to commit and hardest to track-down bad coding faux pas: memory abuse! So, feel free to write recursive functions that pass objects to themselves. It’s kinda like the Star Trek transporters that can’t copy, except for the rare instances like when you’re Tom Riker in the Memory Alpha episode of Next Gen.
So you see, your already terse, readable code is simply less likely to cause memory-leaks by design. A principle similar to duck typing, where you are explicit, but only when you really need to be, is at work here. Things work by convention one way, and get overridden at the moment of the less-frequent exceptions—overtones of what a “framework” is supposed to do. With good conventional default behavior that you only occasionally need to overrule, Python feels much like a framework. Not only is this an advantage for a beginner, but a seasoned pro can use all that saved time to focus on more sophisticated feats than personal library building.
Knowing that as a rule all variable-passing between functions never copies, but rather references the original object in memory is very liberating. It makes you understand a lot more about memory usage in your own code without all that explicit heap, stack, byref and byval nonsense that bogs down the code in other languages and burdens the programmer with precisely the type of housekeeping computers should be good at anyway. You get to intuitively understand the stack, realizing how all un-returned local variables get destroyed when exiting functions. Balloons inflate. Balloons pop. If you don’t carry that balloon around on a string, it’s going to pop. If you want a copy of an object in memory, you explicitly ask for it, and that turns out to be a more efficient place to be explicit than everywhere else in your code. Again, the upshot is that you get more terse, readable code.
Lists are just really well done in Python
Okay, now for the sexy bit. Many languages bill themselves as list manipulators, and have array or vector structures to represent and allow the programmer to interact with them. LISP, PERL and TCL are all known for this, with the L in LISP actually standing for this (and arguably, in PERL as well—pathologically eclectic rubbish lister). Lists in LISP are among the most basic building-blocks, from which almost everything is built-up making powerful meta-tricks possible that programmers like Paul Graham would argue are just not possible in other languages. Unfortunately, you have to be a pulsing-brain alien to use them.
But there is another kind of list—easy-to-use containers for a series of values, often thought of as arrays. But the “list” container can be more versatile than a mere array, ready for countless general-purpose tasks, such as mini-databases of name/value pairs, FIFO/LIFO stacks and more. Such list-like array structures spare you from building them up from scratch in every program you need them (which is always) and inventing possibly inconsistent interfaces to them. Python employs the list concept as well, making it and its close relatives the dictionary (key/value hashes) and tuples (immutable list) into front-and-center features that heavily shape Python’s personality. Here’s making a list then iterating through it:
alist = []
alist.append("spam")
alist.append("eggs")
for item in alist:
print item
This can also be stated as:
alist = ["spam", "eggs"] for item in alist: print item
Python documentation sometimes call lists, dicts and tuples first-class data type objects, like a string or integer, just with plenty of built-in methods like iteration, popping, appending and sorting. All these structures can be arbitrarily nested for complex objects (jagged-arrays in other languages), and represented as ASCII strings (even when containing Unicode) making Python data ridiculously easy to transport—very similar to JavaScript’s JSON notation in which the string representation of an object is so readily swapped with the object itself in memory. Built-in libraries can flipflop your object in memory between JSON and native Python, so JSON-based web service API-work is a snap.
So, we have a dictionary object in our program (which are hashes, or collections of name/value pairs), and we need it to be JSON:
>>> adict = {'spam' : 'delicious', 'eggs' : 'delightful'}
>>> import json
>>> ajson = json.dumps(adict)
>>> print ajson
'{"eggs": "delightful", "spam": "delicious"}'
It’s worth pointing out that single-quotes and double-quotes are interchangeable in both JavaScript and Python, and which you select is by convention, and to encourage clarity. For all intents and purpose, the visual representation of the object in memory is almost identical with Python and JavaScript. If we wanted to convert the JSON back into native Python…
>>> bdict = json.loads(ajson)
>>> print bdict
{u'eggs': u'delightful', u'spam': u'delicious'}
The “u” token is inserted as Python’s way of representing Unicode. Essentially the only difference that happened to our object as it got passed around is that it got “converted up” to Unicode—not the worst thing in the world. While not as awesome as having JSON, the emerging lingua franca of simplified data interchange as your native object language, Python is an extremely good neighbor, making translation a mere formality.
And even though it potentially deserves its own section in this article, I would be remis if I did not mention Python slices, which is basically just a string and object manipulation syntax whereby you eliminate the need for functions like instring, indexOf, left, right, mid, and all those other string-manipulation functions that make grabbing sub-strings so hard. In other words, if you want only the middle of “middle”, you would say “middle”[1:-1], which would chop off the first and last character to produce “iddl”. Slices are yet another one of the strengths of Python, and there is so much more to say here, such as pointing out negative indexes, but the astute reader should be able to infer all that. Slices work on strings and lists, and just makes everything about manipulating text and lists so much cleaner and terse.
>>> astr = 'homeslice'
>>> print astr
homeslice
>>> print astr[:]
homeslice
>>> print astr[:-1]
homeslic
>>> print astr[-1:]
omeslice
>>> print astr[-1:-1]
omeslic
>>> print astr[1:3]
om
>>> print astr[:4]
home
>>> print astr[astr.find('s'):]
slice
The upshot of all this list stuff, is that the data-container mechanisms called for throughout nearly every programming pattern is built into Python, making some of the most common and tedious tasks in programming actually a pleasure to perform. Populate a list. Append and pop values. Grab sub-lists. Pass the whole thing as an argument to a function—no problem. Learn the interface to a list, and you pretty much know the interface to dict’s and tuples. Python quickly turns you into a list loving mad hasher. It’s soooo easy, that many folks make their first project with Python building yet another framework—when in reality you may not even need it because Python is so far along to being a framework already itself.
Again, we encounter the concept here of Python having a lot in common with a “framework”—something usually added on top of a language. The parts of Python are built up just enough towards being a framework to spare you the desire to add yet another level of framework, and by being part of the core distribution, you can rely on these perfectly-sized building blocks being there in every Python program you write. Getting familiar with them, and how to best use them, leads to the “Pythonic” way of thinking. When you achieve it, you will stop having to look for code examples, except of course for learning the interfaces to libraries you haven’t used before.
Yes, larger third-party Python frameworks like django are still there in the wings waiting to be a sort-of Ruby on Rails for Python, but I think I would recommend resisting django for awhile when taking up Python, just to appreciate for awhile how framework-like Python on its own already is. Python strikes this nice framework-balance without going overboard, with a gargantuan Class Library where you have to use obscure dot.this.dot.that notation in an insane sort of scavenger hunt just to do simple things, such as with Java or .NET. Python is just chunky enough, with lists being the tastiest bits. Some people find that where Python falls just shy of being super-easy for Web development, you can add a very lightweight framework like Flask to take you that last mile without having to jump on the Django tank.
Files just become namespaces
Okay, time to talk code manageability. Many computer systems have the concept of modules and namespaces. In Python, these concepts are tied together, and latched firmly onto “files”. So, each file on the hard drive (or whatever) in a Python directory is a module that can be imported into any other file in that directory, automatically getting it’s own namespace that is the same as… drumroll, please… the name of the file! If you see something-dot-something, and the first something isn’t built into Python or a class in your current file, you’re going to find a file by that name on the drive—probably in the folder you’re working in. So in Python, you barely need to understand namespaces to get their benefit—you’re just sort of using them automatically—another big boon for collaboration.
Did I mention importing modules? Okay, here’s the part that blows my mind: you can keep re-importing the same modules all over the place with no loss of efficiency. Unless explicitly stated otherwise, the library is imported only the first time it’s needed, and subsequent imports merely reference the original import to make namespaces available. In other words, file A can import file B and file B can import file A, and there’s no problem with that. This will not create an infinite loop of imports, and in fact, it will have the desirable consequence of file A’s namespace being available in file B and visa versa.
This mutual importing as an encouraged technique to manage namespaces took me awhile to grok, but it was awesome when I did. Resultingly, some code nests module imports deep in a function, assuming the module will only be loaded into memory the first time it’s needed. Conversely, you can write one common file that does all your module imports, and import that in each of your other modules for a global resources approach.
The good, the bad, and the good again
Okay, if you’re thinking Python sounds fun, keep in mind that it was in fact named after Monty Python—and NOT the snake, as is commonly believed. So, instead of foo and bar, you will frequently encounter spam and eggs in example code, along with Knights who say ‘Ni’*3. And although Python was created by a single individual, Guido van Rossum, who managed to get himself hired by Google (which was developed partly in Python), the language is now maintained and advanced as open source by a committee, using Python Enhancement Proposals (PEPs)—with Guido named the benevolent dictator for life. The community is willing to break compatibility with older versions when something really objectively needs to be improved.
One thing that really needs to be improved is the Urllib2 library. Just by its very name, you know that you need to be “in the know” that you shouldn’t be using the original Urllib—which is particularly a shame, because this is the first thing you want to do in Web Service programming. And even so, just to change the useragent of an http request, you have to go making functions that inherit from a superclass and override methods. That really sucks if you don’t know object oriented programming, which can be avoided in all other regards. Making simple http requests is much easier in Ruby, but this can be alleviated by adding the Request module, which I hope will be wrapped into the Python core libraries.
So, Python is not trying to be everything for everyone, and has its occasional annoyance. I think it’s just trying to be many things to most people while minimizing annoyances. Computer science types might be more satisfied with LISP or C that let you get “closer to the metal” to yield higher performance, or meta-languages with more robust support for anonymous functions, like LISP or Ruby, enabling certain things that are nearly impossible in other languages. Also, an optimized high-speed runtime environment isn’t on of Python’s strengths, nor is it undergoing the evolutionary pressure imposed by the browser JavaScript runtime speed arms-race, such as occurring with the Google V8 engine that made node.js possible, and whatever Microsoft and Firefox are doing.
In fact, about the only reason I can think for a newbie to NOT take up Python is that you MUST learn JavaScript at some point, as the defacto standard way to do client-side programming for the Web browser. A year ago, this would not have been compelling enough, because you would still have to pick a different language for the server, but with the new node.js project, JavaScript is once again (since the now obsolete Netscape Server & Active Server Pages) a valid option for server-side programming—and possibly, the one language to rule them all. If your goal is to only ever have to learn one language, JavaScript might be your choice these days, with the advantage that you will be getting somewhat familiar with C and Java syntax—if you see that as an advantage.
And finally, Python’s fun, high accessibility to newbies, open source-ness and willingness to break compatibility would generally make most old-school programmers look on it with suspicion, and consider it not to be a “real” language—you know, one with a super-optimized compiler for creating binary executable. Well uh, while it’s not the norm, you can create binary executable with Python, and even so, it’s not trying or pretending to be C or C++.
But when the time comes to super-optimize your Python code, it does work quite nicely with either C or C++. Both can communicate with Python, allowing Python modules (files) to be swapped off with compiled C/C++ for hotspot optimization. This means that learning Python lets you flesh out applications quickly, speeding up development speed at the sacrifice of application speed. But when application speed becomes the priority, you can start swapping out bits with C, arguably making Python a productive step on the way to C.
Python is a language for which a newbie and pro alike can love. It can be a stepping-stone to C or C++, or an end in-itself. As an end in-itself, Python is different from most any other language, but in ways that yields great advantage, making it actually worth diverging from the mainstream. Yet, Python still manages to hang on as a mainstream language.
I’m not a professional programmer, but nearly everything I do directly demands programming, so I need a language I can take up again between long stretches of inactivity, without feeling like I have to chase the rabbit down into Wonderland to be productive again (as you do with languages with lots of overhead). Plus, following what I can only imagine to be a similar line of reasoning, Python is the first language to break LISP’s 20-year stronghold on MIT’s legendary introduction to computer science course.
It seems that in almost every discussion LISP is the exception to the rule—which is both its greatest strength and weakness. If you’re ready to chase that rabbit down the hole into Wonderland, and become a professional programmer with super-powers, then LISP may be your best choice. If you’re just getting started and you want to only ever have to learn one language, then JavaScript is your best option. However, if you are a casual non-professional programmer like me, and want to simply love the opportunity to take on the occasional programming project and want super-powers when you do, then Python may be for you.
Thanks for sticking it out through such a long love-affair article about Python. I love it so much, that I decided to make a really, really, really easy way to try it out that sets you on an actual path towards Python system programming on a Linux server. So, whether you’re on Windows, Mac or another Linux, just download this ~15MB file, unzip it, and double-click it, and you’ll have a tiny virtual Linux server on your desktop setup to begin teaching you Python. Have fun!












{ 3 comments… read them below or add one }
Huge fan of Monty Phyton too! You have some really interesting stuff in here, I’ll recommend it to some of my friends who work in the programming area!
By the way, that’s a great reason to make a better world.Keep up the good work.
Really great work. loved the part where u using pics to make it more clear. i hav started blogging in python. it will be great help if u give suggestions on that.
What an article !!! Loved it . . . .
{ 3 trackbacks }