Python: an introduction for PHP (and other) programmers

I love Python. It is clean, it simple and it evolves in a natural and healthy way. I have been working with PHP for over 10 years now, but Python stole my heart. Ahhhww :P

Here are some pointers to get you started, and hopefully make you enthusiastic too. Of course this is just a little tip of the iceberg. I can’t give you a course in Python, but it should give you the push to explore a bit further.

Please note that this tutorial is primarily targeted at Python 21, but the examples all should work in Python 3 without trouble.

The interpreter

Before telling more about the language, you should know that the python interpreter has a great interface for use in the shell. You can type in statements, which are interpreted on the fly. Results of evaluations are printed to stdout, so you can see the result directly:

$ python
>>> 1
1
>>> type(1)
<type 'int'>

To var_dump an object as in PHP, Python has a non-recurring variant dir(), which will show you all properties an object has.

>>> dir(1)
['__abs__', ..., '__xor__']

Here you can see 1 has all the methods that are defined in the int type. 1 is, in fact, an instance of the int type, with value 1:

>>> (1).__str__() # __str__ is equivalent to PHP's __toString()
'1'

It is useful to know throughout this post that every name in python is in fact an object, whether it is a constant, a literal, an object, class, function, or type. This is similar to JavaScript, which you might be familiar with.

Code structure

Structure is based on indentation. This means that you will no longer use curly braces for compound statements and class or function bodies, but you will indent another step, similar to how YAML works. How big this indentation is, you decide. I use 4 spaces, but you may use tabs, or other amounts, you can even mix indentation styles. Of course it is best to choose just one style and stick with it.

def toBeOrNotToBe(a):
    if a:
        print(a, "is")
        print(not a, "is not")
function toBeOrNotToBe($a) {
    if($a) {
        echo $a, " is\n";
        echo !$a, " is not\n";
    }
}

Notice you don’t need parentheses around the if’s expression, and the blocks are never closed (unless a new line with lower indent is encountered). Also notice you don’t need a statement terminator besides a newline, like you need the semicolon in PHP.

Be aware that print is a special case statement in Python 22, not a regular function call. This is similar to php’s echo.

If you need an empty block, you’d use the $pass$ statement:

def thisDoesNothing():
    pass
def thisNeither():
    if False:
        pass

You may keep a block at one line, as long as it poses no ambiguity to the parser. Therefore

def thisDoesNothing(): pass

Is the same as the above version, but

def thisNeither(): if False: pass

renders a syntax error. The following version, however, is ok:

def thisNeither():
    if False: pass

Control structures

Flow control

If-statements work as follows:

if a:
    print(a)
elif b:
    print(b)
else:
    print("?")
if($a) {
    echo $a;
} elseif($b) {
    echo $b;
} else {
    echo "?";
}

Note that the following piece of code would render a syntax error in python.

if a: pass
else if b: pass
if($a) ;
else if($b) ;

Switch statements don’t exist.

Loops

The C-style for-loops don’t exist in Python. You should use sequences to iterate over. Sequence types are more thoroughly explained in the documentation, but for now it suffices to say that a sequence is best compared to a (non-associative) array in PHP.

Python-style for-loops are best compared to PHP’s foreach. Following code, using a range of numeric values as a sequence iterated over, is equivalent:

for i in range(0, 10):
    pass
foreach(range(0, 10) as $i)
    ;
// or
for($i = 0; $i < 10; $i ++)
    ;

Both would translate into the following C-style for-loop:

int i;
for(i = 0; i < 10; i ++)
    ;

While loops are present in Python, with effectively the same syntax as in PHP:

i = 0
while i < 10:
    print i
    i += 1

but do-while loops aren’t.

Comments

Comments start with a hash (#) and end with a newline (just like PHP’s # and // comments). Comments spanning multiple lines don’t exist.

Built-in types

First of all, you can check types of variables by using type(). Try using the interpreter to find out how types are handled.

I won’t cover all types, but basically the most common 6 built-in types are:

  • None, which is best compared to PHP’s null
  • booleans, which are either True or False
  • numbers: int, long, float and complex
  • sequences: string, list, tuple (comparable to PHP’s array) and str (which is the type() of a string)
  • mappings: dict, best compared to an associative array in PHP
  • functions: either named, defined with def, or lambda expressions
  • classes
  • All other instances. Since in fact everything is an instance of some type, all remaining instances are typically object instances of user space classes.

Variables

Variables have no special prefix as in PHP. All variables are of the same order, which means that a function is a variable too, so is a class, so is True, so is NoneType which is the type() of None.

Operators

Operators work pretty much the same as in PHP. Some differences are:

Operation Python PHP
String concatenation a + b $a . $b
Boolean expressions not a or b and c !$a || $b && $c
Member access obj.property $obj->property

Python has a few added nice features with operators, e.g. slicing in sequence types using the brackets operators:

Python PHP
a = [1, 2, 3] $a = array(1, 2, 3)
b = a[1:2] $b = array_slice($a, 1, 1)

(Note that Python uses indexes rather than lengths for the second argument)

… and in Python 2, the string formatting operator:

Python2 Python3 PHP
a = "string %d formatted" % 1 a = "string {0:d} formatted".format(1) $a = sprintf("string %d formatted", 1)

Functions

Functions are defined using the def keyword. A function consists of a name, an argument list and a body. The simplest function definition is as follows:

def f(): pass

The argument list may contain positional arguments with or without default values (which are grammatically equivalent to PHP arguments), keyword arguments with or without default values (which means that you can juggle the argument order around when calling the function) and a list of arbritrary arguments or keyword arguments, absorbed by a tuple or a dict at call time.

def fn1(a, b): # regular, positional arguments, and required by default
    print (a + " and " + b)
 
def fn2(c="c"):
    print (c)
 
def fn3(*args, **kwargs):
    print (args)
    print (kwargs)
 
fn1("a", "b")       # "a and b"
fn1(b="b", a="a")   # "a and b"
fn2()               # "c"
fn2("d")            # "d"
fn3()               # () {} (an empty list and an empty dict)
fn3(1, 2, 3,
    a="foo", b="bar") # (1, 2, 3) {'a': 'foo', 'b': 'bar'}

Functions may return and yield values. Yielding values makes the function a generator, which is a compile-time feature of python, basically meaning you can iterate over multiple variables returned by a function, breaking the current execution of the function itself. Again, examples tell more. The range function could be implemented as follows:

def range(start, end):
    i = start
    while ( i < end ):
        yield i
        i += 1
 
for i in range(5, 8):
    print(i)

Resulting in:

5
6
7

Classes

  • The main difference between python and PHP classes is that Python’s classes are mutable. This means that at any given time in the execution of a script, the class’s internals can be altered. This is somewhat like in JavaScript, where you can alter an object’s prototype, which in fact alters the behaviour of all instances based on that prototype.
  • Second, Python knows no access modifiers. Public, protected and private are in no way forced. The rule of thumb is that anything prefixed with a single underscore is considered private. The rest is public. But this is a convention, nothing forced by Python itself. Python doesn’t care if you want to change private variables. If that’s what you want to do, go ahead ;). Abstraction, such as abstract methods and interfaces, are not available. This leads to another fun aspect of Python, it has no compiler-level type checking. This is all runtime, and only if you want to do something that involves typing, you can write the code yourself (e.g. using isinstance())
  • Third, classes support multiple inheritance, which means that a class can derive it’s methods and properties from multiple parent classes.
  • Fourth, all methods get the current object’s instance as a first parameter. This means that calling the method statically with an object instance as the first parameter would render the exact same results as calling the method dynamically on the object instance. Convention is that you call the current instance self.

There is also a funny similarity between Python and PHP classes. In python, special methods have the form __method__, where “method” is the special name in case. In PHP, we know the same thing for e.g. __toString(), __call(), etc.

The following is an example of classes in Python.

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y
 
class Circle(Point): # let the class Circle inherit from the class Point
    def __init__(self, x, y, radius):
        super(Circle, self).__init__(x, y) # Explicitly call the parent constructor
        self._radius = radius
 
    def __str__(self):
        return "This is a circle"
 
def another_str_impl(obj):
    return "It is a " + str(type(obj)) + " instance";
 
c = Circle(10, 10, 10)
print(c) # "This is a circle"
 
Circle.__str__ = another_str_impl;
 
print(c) # It is a <class '__main__.Circle'> instance

As you can see, the ‘init‘ is the constructor, __str__ is more or less the same as PHP’s __toString(). Also note the omission of a new keyword; Pythons classes are called directly.

The following table shows a mapping of PHP’s magic methods to Python’s

PHP Python
__get __getattr__
__set __setattr__
__isset not available
__unset __delattr__
__call not available
__construct __init__
__destruct __del__
__invoke __call__
ArrayAccess::offsetGet __getitem__
ArrayAccess::offsetSet __setitem__
ArrayAccess::offsetExists not available
ArrayAccess::offsetUnset __delitem__

Also, implementing Iterators like in PHP is even easier in Python. Simply implement the __iter__ method generating all items, and you’re good to go:

class A:
    def __init__(self):
        self._items = ["a", "b", "c"]
 
    def __iter__(self):
        self._items.reverse();
        for i in self._items:
            yield i
a = A()
for i in a:
    print(i)
 
for i in a:
    print(i)

Result:

c
b
a
a
b
c

Ultimately, you can recreate virtually all types with the magic methods in Python. See the entire list in the documentation.

Modules

A module in Python is a file containing code. The name of the module is the filename, and the code can be used in other files by importing the modules. The file run from the command line is called the “main” module, which makes the name of the special __name__ variable contain "__main__".

file1.py:

import file2
from file3 import bar #import bar from file3 as a local name 'bar'
 
print(__name__)
print(file2.foo)
print(bar)

file2.py:

foo="foo"

file3.py:

bar = "bar"

Running file1.py from the command line would give the following results:

$ python file1.py 
__main__
foo
bar

The most common practice is to put classes into modules which are then imported in other modules. It is considered bad practice to have files run code that is not needed for their bootstrapping, so main code is usually fenced inside an if, checking main is the current scope:

file1.py:

import file2
if __name__ == "__main__":
    print("file1.py says hi")

file2.py:

if __name__ == "__main__":
    print("file2.py says hi")

Running these file from the command line:

$ python file1.py
file1.py says hi
$ python file2.py
file2.py says hi

Decorators

Decorators are functions or classes that can change a piece of code run-time, but does not clutter the code. This makes for excellent aspect-oriented programming, being able to mix in any kind of functionality into your class or function. I’ll just leave it with a simple example:

debugging = False
 
def traced(fn):
    def decorated(*args, **kwargs):
        if debugging:
            print("Enter: " + fn.__name__ + "()");
        fn(*args, **kwargs)
        if debugging:
            print("Exit: " + fn.__name__ + "()");
    return decorated
 
@traced
def foo(bar, baz):
    print("bar: ", bar, "\nbaz: ", baz)
    foo2(bar, baz)
 
@traced
def foo2(bar, baz):
    pass
 
foo("hello", "world");
debugging = True
foo("world", "hello!");

The output would be:

$ python ./test2.py
bar:  hello 
baz:  world
Enter: foo()
bar:  world 
baz:  hello!
Enter: foo2()
Exit: foo2()
Exit: foo()

Docstring

Python has a language feature which let you document classes and functions by inserting a string between the declaration and it’s definition. This documentation is available at runtime in the __doc__ property.

def fn():
    "This function actually does nothing"
    pass
 
print fn.__doc__ # prints the docstring

This makes it very easy to get documentation on code using the interpreter:

>>> import os
>>> print os.path.realpath.__doc__
Return the canonical path of the specified filename, eliminating any
symbolic links encountered in the path.

… and much, much, much, much, much more…..

This post could continue for ages. But I’m going to stop here. I hope I have given you an appetite. Python is one of the most flexible languages out there, and I’d really encourage you to try something with the Django web Framework, Google App Engine, or get started building GTK or QT desktop applications, binding to VLC’s library, or write your own threaded web server, the possibilities are endless.

If you’re getting enthusiastic, just go monk and simply read the Python documentation.

Have fun :)


  1. Python 2 is rapidly becoming outdated, but let me tell you that learning python 2 won’t be wasted time. Code samples in this post work in both python 2 and 3. 

  2. In Python 3 this actually became a regular function, so all print calls in the examples have parentheses, even though they’re not needed in Python 2. 

This entry was posted in Development and tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

One Comment

  1. Wouter
    Posted December 12, 2010 at 13:54 | Permalink

    Boolean expressions – not a or b and c – !$a || $b && $c

    Just a small note but I would allways use parentheses here for readability.

One Trackback

  1. [...] Dit blogartikel was vermeld op Twitter door Rick Mans, Gerard van Helden. Gerard van Helden heeft gezegd: Just posted: http://melp.nl/2010/12/python-for-php-programmers/ [...]

Post a Comment

Your email is never published nor shared.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">