low-level python hacker and author of RPyC,Construct and Plumbum. writes
about python, algorithms,
software architecture and whatever else comes to mind.
A Python Crash Course for the Statically Typed Programmer
November 15, 2013
Python is a multi-paradigm (hybrid) language. It's fully object-oriented but has strong functional roots.
Note that this isn't a [beginner's tutorial](http://learnpython.org/) but a quick reference for the language
and its features that should allow you to write basic Python ASAP.
If you had taken any academic course that involves programming, Python will most likely resemble pseudo code to you
You'll see inheritance and class diagrams along side with constructs imported from [Haskell](http://www.haskell.org)
and [LISP](http://en.wikipedia.org/wiki/Lisp_%28programming_language%29).
Python is dynamically-typed (as opposed to statically-typed) but has strong-typing
(as opposed to Perl or Javascript)
Syntax reference (1)
```
# printing to stdout
print EXPR
# assignment
VAR = EXPR
# conditions
if COND:
SUITE
[elif COND:
SUITE]
...
[else:
SUITE]
# for-loops
for VAR in ITERABLE:
SUITE
[break]
[continue]
# while-loops
while COND:
SUITE
[break]
[continue]
```
Syntax reference (2)
```
# try-except
try:
SUITE
except EXCEPTION [as ex]:
SUITE
...
[else: # iff there was no exception
SUITE]
[finally: # always be performed
SUITE]
# raising exceptions
raise EXCEPTION(...)
# importing modules or attributes
import MODULE
from MODULE import NAME
# defining functions
def NAME([a, [b, ...]]):
SUITE
[return EXPR]
[yield EXPR]
# defining classes
class NAME([BASE, [...]]):
SUITE
```
The interactive interpreter is your friend! I mapped ``F11`` on my keyboard to fire up an interpreter...
it's better than any calculator you'll ever have
```
$ python
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 5 + 6
11
```
For people with a C background:
* You don't declare variables - you just assign them
* And you can reassign them to different types
* Python doesn't have ``do-while`` (use ``while``) nor does it have ``switch`` (use ``if``s or
dispatch tables)
* Assignment is a statement, **not an expression**.
* You cannot write ``if (a = foo()) == 5:``
* There's no such thing as passing by-value. It's always by-reference.
* So you might need to explicitly **copy** objects some times
* Or (preferably) create new objects from existing ones
For people with a C++/Java background:
* **Everything** is a first-class object
* Integers, functions, types, stack frames, tracebacks, etc.
* There are no privates, only conventions. Members that start with ``_`` are not to be manipulated directly.
* There's no ``new``, just *invoke* the class like a function.
* ``inst = MyClass(1, 2, 3)``
Duck Typing goes Nuclear
In low-level programming languages, types dictate a **memory layout**.
In high-level languages, on the other hand, compile-time types are merely **constraints**
on what you're allowed to do with an object.
Being an interpreted language, Python gives up on type-checking and instead adopts the spirit of
"it's easier to ask for forgiveness than permission". Just try and see what happens.
Python comes with lots of useful built-in types
String manipulation is a pleasure with Python
Encoding strings like a boss
String interpolation
And joining strings is surprisingly useful
Multi-line strings
If I had time to show you only three functions, they will be
* ``help()``
* ``dir()``
* ``type()``
Everything else you can discover on your own.
Next, let's meet some types and learn how to convert (not **cast**) values from one type to the other
Types matter
Repr(esenation)
Lists
Slicing
Lambda functions
Working with files
Working with resources
The ``with`` block automatically closes the file. It's more general than this, of course, as it's
not limited to files. It simply ensures that the necessary cleanup that's associated with the object
will be performed when you finish the ``with`` block.
For example, you can use ``with`` to commit a transaction to a DB or do a rollback on failure,
etc. It's a very useful pattern.
List comprehension: remember ``map`` and ``filter``? Well, it's time to forget about them
Yo comprendo!
And the whole multiplication table in just one line!
Chillex! Be lazy
Generators let us be as general as we wish while paying only for what we're actually using (lazy evaluation)
The ``itertools`` module has some nifty utilities that we can use
Remember list comprehensions? Forget them too. Generator comprehensions are the new black!
Huh? Of course you won't see anything... you have to consume the generator in order for it to produce values.
In fact, list comprehensions are a syntactic sugar for ``list(``generator comprehension``)``
List comprehensions, as the name suggests, **build a list**. This can be expensive some times,
especially when you don't need the intermediate values. E.g., if you just want to get the sum of the elements,
there's no need to actually hold all of them in memory. Generators are the key to efficient programming.
For example, ``xrange`` is like ``range`` but returns a generator instead.
## Object Oriented, Too ##
Don't worry though. Python isn't all geared for functional programming. We have classes too!
Some notes for Java folks:
* Constructors are inherited! Why wouldn't they?!
* You can implement an exception class in Python in just one line: ``class MyException(Exception): pass``
* In Java/C# you have to reimplement all constructors as well
* Python doesn't have function/method overloading
* But given that most of the code is duck-typed anyway (and can introspect the received object at runtime)
it doesn't make a real difference
But if we stopped here you'd think it's just a variation of Java. We're way cooler than Java!
For example, instances (such as ``rex`` above) are actually just dictionaries that hold the instance's attributes.
That's why we can add attributes on the fly
But classes ain't no different! They are just dictionaries of their methods
Now it's time to discuss **special methods**. You've seen them already when we invoked ``dir("hello")`` -- they
take the form of ``__xxx__``, and surprisingly or not, they make up most of what we've seen so far.
Virtually all language constructs map to special methods underneath:
* ``a + b`` is actually ``a.__add__(b)``
* ``a[x]`` maps to ``a.__getitem__(x)``
* ``str(a)`` invokes ``a.__str__()``
* ``a.b`` translates to ``a.__getattr__("b")``
* ``f(a,b,c)`` runs ``f.__call__(a, b, c)``
* So yes, ``f.__call__.__call__.__call__(a, b, c)`` as well
* ``Dog("foo")`` creates the instance using ``__new__`` and initializes it in ``__init__``
You can invoke them yourself, of course, they're just methods:
But why would you do that? Don't be silly.
Remember I said **everything** is an object? Well, I meant it.
And it does gets mindboggling
The MRO (method resolution order) is actually very important. It determines what happens when you resolve
attributes (``__getattr__``) on an object. For instance, ``rex.foo`` will first try ``rex.__dict__``, move up
to ``Dog.__dict__`` and then to ``Animal.__dict__``, until we reach ``object.__dict__`` where we'll fail.
And that's the whole object model.
Dictionaries are also crucial to functions. A function is basically just code that evaluates in a local
scope and has access to a global scope (module-level). Each of these scopes is... a dictionary. When you assign
a variable, you basically insert an element into the scope dictionary.
And no Python tutorial can do without
## When in Doubt ##