Just What is Python? My Initial Thoughts

As I've made some progress on my Python adventures, now may be a good time to express some of my initial opinions on the language.

Fırst of all, I've realized some of my beginner frustrations would have been greatly eased if I had understood just what kind of language was Python. So, I will try to list some primary concepts here:

Please correct me if I've made some incorrect statements below...

Python ...
  • is Interpreted
    • You can directly edit code in place, no compilation phase
  • is Compiled 
    • Yes, I know I've said no compilation :) But it is automatically compiled into a bytecode (.pyc) for efficiency
    • Also for great information on python compilation, GUI, and stand-alone applications, look at this discussion.
    • Also, take a look at Jython, IronPython, Psyco.
  • is Interactive
    • You can open an interactive session and directly chit-chat with the Python itself for quick testing etc.
  • is Object-Oriented
    • Everything in Python is an object, everything! Not really understanding this in the beginning was the source of some big suprises for me. I mean, number 5 is an object too!
    • Read here for a tutorial on Python Objects
  • is Dynamically-Typed
    • You don't need to explicitly define the type of anything, it is just derived and assigned in the run-time context 
  • is Strongly-Typed
    • Just because it is Dynamically-Typed doesn't mean it is also Weakly-Typed. Python variables are Strongly-Typed which means you cannot change the type of something freely without proper type-casting.
  • is Whitespace-Sensitive
    • A controversial issue. Not every whitespace, but the initial indentation is important for blocks.
    • This defense of whitespace is a good read and recommended.
  • is Extensible
  • is a Glue-Language
    • It is used everywhere these days between other systems. But I am not sure exactly which features contribute to this; anyone knows ? I didn't read it yet, but this may help.
  • is Compact
    • Compact in the sense of source-code. How compact? About 1/5 of the equivalent Java code. 
    • A nice graphic shows this easily below (source): 
  • is Rapid
    • You can much rapidly put together something in Python compared to some other languages. I've experienced this first hand, it's eerie really, I don't know how it does that :) 
    • Another nice graphic showing just that can be seen below:
  • is Slower
    • It's runtime performance is not something to show off.
    • In this in-depth scientific comparison you can see it for yourself
  • has Modules
    • Modules = Packages in Java
  • has Static-Scoping
    • Scopes of variables are determined statically in compile-time
  • has Exceptions
    • Good old exceptions
  • has Mutable and Immutable Objects
    • This one is important and another one of my frustration points in the beginning. Mutable/Immutable objects behave differently and sometimes unexpectedly
    • The 10 Pitfalls of Python can be of help here.
  • has Operator Overloading
    • Enough said... There is a quick tutorial on operator overloading in Python.
  • has High Level Dynamic Data Types
    • This is something to be seen; these are really powerful.
    • Look at dictionaries, tuples, lists, generators, list comprehensions etc.... any others I'm not familiar with?
  • has Dynamic Classes
    • This is a point I'm not feeling at home currently. If you are coming from a Java background you may be suprised. Look and see for yourself.
    • Question for experts: can we dynamically add methods and properties to an object in Python and is this thing what the so-called Duck-Typing is about ?
  • has Clear Syntax
    • Python code looks cleaner with fewer parantheses, semicolons, variable definitions etc.
  • has Powerful Libraries (kitchen-sink included)
    • It even has a web server as a library, how cool is that?! I am still pretty much a newbie in the library knowledge, but it looks huge.
  • has Powerful Interfaces (system calls, window systems)
    • In my project, I've used PyQT interface to define a QT GUI and QGIS Python bindings to interface with Quantum GIS. There are many other interfaces, bindings, GUI options, and operating system calls out there. One of the strongest points of Python.
  • has Multiple Inheritence
    • Yeah, it has.
  • has Name Binding Paradigm and Pass-by-Value
    • This is another important concept that understanding would avoid creating some nasty bugs. Every variable assignment should be thought of as a name-binding in Python. This really helps in clarifying some anomalies. Parameters are also passed by values which are themselves references (as in Java). But when immutable objects are passed-by-values-as-ref and then you try to change them inside the function, a new copy is created by Python. Mutable objects don't do this, so you can change the value of the object as if passed-by-reference.
    • Here is a good explanation of pass-by-value effect
    • Read How to Think Like a Pythonista for a fun, in-depth explanation of Name Binding Paradigm

Here are some code examples from the official tutorial by Guido van Rossum and my opinions:

# Fibonacci series: the sum of two elements defines the next
a, b = 0, 1
while b < 10:
    print b
    a, b = b, a+b

This is just some tiny little syntactic-sugar that is lovely and very useful. All the right hand side is evaluated first before the assignments, keep in mind. This actually uses Python's auto-list-packing-unpacking feature.

def f(a, L=[]):
    return L

print f(1)
print f(2)
print f(3)

This will print

[1, 2]
[1, 2, 3]

The above example has nearly creeped me out when I've first seen it. We assign L a default value, a list, by L=[] . But default values in functions are only evaluated once. When the default is a mutable object like a list, this behavior occurs.

d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}

I really like that crazy function calls. It's a breeze of fresh air after coming from highly strict languages. You can do many other things while calling functions, not just that mind you.

b = a[5,10]
b = a[3,-2]
b = a[:]

These are all slices. One of the cool features of sequences. The last one copies the entire sequence.

def func(arg):
    arg = ['another value']
def func2(arg):
    arg[0] = 'another value'
value = ['original value']
func(value)     # this gives ['original value']
value = ['original value']
func2(value)     # this gives ['another value']

Here is a great example showing pass-by-value and the effect of mutable objects. As seen from the output of func(), variables are passed by value and the function does not change the parameter. But then, in the func2() example, the parameter is changed inside the function.

The difference is, in the first one we create a new list and give the reference to arg, but since arg is passed-by-value (which itself is a reference), upon exit, arg gets its original reference back and the newly created list is destroyed. In the second one we do no change the reference itself, just the mutable object it refers to. This makes all the difference. This had suprised me greatly in the past, now it feels natural :)

1 .__add__(2)

Python's everything is an object paradigm is in effect above. I think it is clear enough. The additional space in the integer example is for Python to distinguish it from float literals.

a < b == c

Another neat time saver. This tests both comparisons.

>>> for x in range(1,11):
    print '{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x)

1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

This range() function is another one my favourites in Python.

with open('/tmp/workfile', 'r') as f:
    for line in f:
        print line

A great way to make sure opened files are closed properly and automatically.

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)
class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g

In this example, function object assignment is shown. You can assign a function to a new name and use it as like that. Also here, f1 becomes a class method. As GvR noted for this example, "this practice usually
only serves to confuse the reader of a program" :))

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
    def addtwice(self, x):

As you can see class definitions are usually cluttered with the word 'self'. It is actually a reference to the object itself and every class method should have it as the first parameter (it can be named as something else). I am not sure I've liked this practice much though. The code quickly becomes 'ego-centric'. Jury is out for me on this one for now.

    for index in range(len(data)-1, -1, -1):
        yield data[index]

>>> for char in reverse('golf'):
        print char

This one uses so-called 'generators', one of the greates tools Python offers. A generator basically returns a sequence, but it does not return all of the sequence at once. Every call to it resumes operation from the last 'yield' point. It works like a 'thread', cool !

line_list = ['  line 1\n', 'line 2  \n', ...]

# Generator expression -- returns iterator
stripped_iter = (line.strip() for line in line_list)

# List comprehension -- returns list
stripped_list = [line.strip() for line in line_list]

Another example for generators and also for the list comprehensions which are neat syntactic-sugars for list generation.


As a final tip, I recommend Code Like a Pythonista: Idiomatic Python for anyone wanting to learn the Python way..

This post gotten out of hand somewhat in length, so I better tidy up now. I hope this was helpful or interesting for the other beginners like myself. I feel that my mind is still not able to 'think in Python' and sometimes shoot myself in the foot while there are easier ways. I hope to be able to get that perspective in time.


summary: 'understanding paradigms makes complex things managable'
mood: madchuckle in wonderland

1 comment: