view PythonForJsProgrammers.txt @ 16:a53f8b3f8666

Added section on the import statement.
author Atul Varma <varmaa@toolness.com>
date Thu, 05 Jun 2008 20:18:51 -0700
parents 01712d2193d3
children b4d176c2bf39
line wrap: on
line source

=================================
Python for JavaScript Programmers
=================================

I couldn't find anything on the web that attempted to teach Python to
readers who already knew JavaScript, so I thought I'd give it a shot,
since a number of my friends at Mozilla don't know much about Python
but know JavaScript incredibly well.  The languages actually aren't
that dissimilar--in fact, some of JavaScript's latest features have
been `borrowed directly from Python`_.

.. _`borrowed directly from Python`: http://weblogs.mozillazine.org/roadmap/archives/2006/02/js_and_python_news.html

Whitespace
==========

This is coincidentally a good place to explain Python's design
philosophy; hopefully it will give you a better idea of whether this
is a language you'd like to use.

While not syntactically enforced by many languages, whitespace is
semantically meaningful during the reading and writing of code.  Take
the following example of C-like code::

    if (someVar == 1)
        doSomething();

The line ``doSomething();`` is indented after the ``if`` statement to
indicate that it should only be done if the statement it's below is
true.  Given this, consider what the following code does::

    if (someVar == 1)
        doSomething();
        doSomethingElse();

It's clear from the use of whitespace that ``doSomethingElse();``
should also only be executed if the statement it's indented under is
true, but this is not the case for C-like languages.  Indeed, the
programmer must add additional code to tell the compiler what he or
she means::

    if (someVar == 1) {
        doSomething();
        doSomethingElse();
    }

Why does the programmer have to write more code to tell the computer
something it should already be able to understand from the use of
whitespace?

This is actually a violation of the `Don't Repeat Yourself`_ (DRY)
principle popularized by Andy Hunt and Dave Thomas.  Because unneeded
extra work is required when moving from a single-line clause to a
multiple-line clause, it's a constant source of errors in C-like
languages, and many stylistic rules and arguments have been spawned as
a result of this mistake in language design.

Python is one of the few languages that takes the simpler and more
humane approach: whitespace has a consistent semantic meaning to the
humans who write code, so the computer should take this into account
when it processes the code.  This reduces the burden on the programmer
from having to repeat their intent in multiple different ways.

So, you won't see any brackets in Python.  Instead, if a statement ends
with a colon, the next statement needs to be indented and begins a new
block.  The block ends as soon as a line is encountered that's
unindented at least one level, like so::

    if someVar == 1:
        doSomething()
        doSomethingElse()
    else:
        doOtherThing()

Also note that Python doesn't use semicolons, which is yet another
language feature that reduces the cognitive burden on the programmer.
Indeed, many of the language features covered below were designed with
a very careful eye towards readability, reducing cognitive load, and
making the process of programming `as enjoyable as possible`_.

.. _`Don't Repeat Yourself`: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
.. _`as enjoyable as possible`: http://xkcd.com/353/

The Interactive Shell
=====================

Python, when executed with no parameters, just presents an interactive
interpreter.  It's similar to the SpiderMonkey shell and xpcshell if
you're familiar with those.  All following code examples in this
tutorial will be displayed as though they're being executed in it,
like so:

    >>> 1 + 2
    3
    >>> # Here's a comment that does nothing.
    >>> print "hi!"
    hi!
    >>> print "This is a long " \
    ...       "statement that spans multiple lines."
    This is a long statement that spans multiple lines.

One built-in function in particular that helps explore things in the
built-in shell is ``dir()``, which returns a list of all the
attributes attached to an object:

    >>> dir("a string")
    ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

If there's a function you're interested in learning more about, you
can look at the built-in documentation metadata associated with the
object--known as the `docstring`--by querying the object's ``__doc__``
attribute:

    >>> print "a string".join.__doc__
    S.join(sequence) -> string
    <BLANKLINE>
    Return a string which is the concatenation of the strings in the
    sequence.  The separator between elements is S.

This makes it very easy and fun to explore the language and its
environs.

Batteries Included
==================

Python comes with a standard library that provides a great deal of
functionality, from enhanced introspection to serialization, XML
processing, database access, testing, networking, data archiving, and
more.  Extensive documentation for it all is contained in the `Python
Library Reference`_.

To use the functionality of a module, you'll use Python's ``import``
statement, like so:

    >>> import sha

This particular line imports the `sha`_ module, which provides access
to the SHA-1 message digest algorithm.  At this point, ``sha`` is an
object in your namespace and can be used, for instance, to create a
``sha`` object from which to generate a hex digest:

    >>> sha.sha("hello").hexdigest()
    'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'

.. _`Python Library Reference`: http://docs.python.org/lib/lib.html
.. _`sha`: http://docs.python.org/lib/module-sha.html

Expressions
===========

Python's expression syntax is much like that of JavaScript, or any
C-like language for that matter:

    >>> 9 & 1              # Bitwise operations
    1
    >>> 2 << 2             # Shifting
    8
    >>> 5 >= 3             # Comparisons
    True
    >>> 8 + 2 * (3 + 5)    # Arithmetic
    24
    >>> 1 == 1             # Equivalence
    True

Some C-like expression constructs have been substituted for more
readable alternatives:

    >>> not True           # 'not' instead of '!'
    False
    >>> True and True      # 'and' instead of '&&'
    True
    >>> True or False      # 'or' instead of '||'
    True

But there's some elements of C-like expressions that aren't supported,
because they tend to be more trouble than they're worth.  The ``++``
and ``--`` unary assignment operators aren't part of the
language, and nor is JavaScript's ``===`` comparison operator.

Some constructs that can be used in expressions in C-like languages
can only be used in statements in Python:

    >>> a = 5              # Assignment works in statements.
    >>> a += 1             # Add-assignment does too.
    >>> if a = 1:          # But you can't assign in an expression.
    ...     pass
    Traceback (most recent call last):
    ...
    SyntaxError: invalid syntax

Nothingness
===========

Unlike JavaScript, Python doesn't have a concept of ``undefined``.
Instead, things that would normally cause ``undefined`` to be returned
by JavaScript simply end up raising an exception in Python:

    >>> "a string".foo
    Traceback (most recent call last):
    ...
    AttributeError: 'str' object has no attribute 'foo'

In most cases, this is for the best, as it makes debugging easier.  If
you really need to find out if an object has a particular attribute,
however, you can use the ``hasattr()`` function:

    >>> hasattr("a string", "foo")
    False

Python also has an analog to JavaScript's ``null``: it's called
``None``.

Functions
=========

Functions are defined like so:

    >>> def foo(x):
    ...     print "foo called with parameter: %s" % x

They are called as you'd expect:

    >>> foo(5)
    foo called with parameter: 5

Unlike JavaScript, though, it's not possible to call them with fewer
or more arguments than they'd expect:

    >>> foo()
    Traceback (most recent call last):
    ... 
    TypeError: foo() takes exactly 1 argument (0 given)

Though it is possible to provide defaults for arguments:

    >>> def bar(x, y=1, z=5):
    ...   return x + y + z

And it's also possible to specify arguments using keywords:

    >>> bar(1, z=6)
    8

You can also write documentation for functions by providing a string
immediately following the function signature:

    >>> def foo():
    ...     "Does something useless"
    ...     pass

As mentioned earlier, this string is called the `docstring`, and is
actually attached to the function object as its ``__doc__`` attribute.
Creating docstrings for your functions not only helps document your
code, but also makes it easier for Python users to interactively
explore your code, too.

As in JavaScript, Functions are first-class citizens and can be passed
around as parameters to other functions and so forth.

Global Variables
================

Python, like JavaScript, is lexically scoped when it comes to reading
variables.

However, Python's scoping rules for assignment to undefined variables
works opposite to JavaScript's; instead of being global by default,
variables are local, and there is no analog to ``var`` or ``let``.
Rather, the ``global`` keyword is used to specify that a variable be
bound to global instead of local scope:

    >>> a = 1              # Define our global variable.
    >>> def foo(x):
    ...     a = x + 1      # 'a' is a new local variable.
    >>> def bar(x):
    ...     global a       # Bind 'a' to the global scope.
    ...     a = x + 1
    >>> foo(5)
    >>> a
    1
    >>> bar(5)
    >>> a
    6

This is for the best: as it's well-known that global variables should
be used as sparingly as possible, it's better for a language
interpreter to assume that all new assignments are local unless
explicitly told otherwise.

Closures
========

Function closures are available in Python:

    >>> def myfunc():
    ...     a = 1
    ...     def wrapped():
    ...         return a
    ...     return wrapped
    >>> myfunc()()
    1

Unlike Javascript, however, the variable bindings in the closure are
"read-only":

    >>> def myfunc():
    ...     a = 1
    ...     def wrapped():
    ...         a += 1                 # Doesn't work!
    ...         return a
    ...     return wrapped
    >>> myfunc()()
    Traceback (most recent call last):
    ...
    UnboundLocalError: local variable 'a' referenced before assignment

This means that closures can't be used to access private variables
like they can in JavaScript; instead, everything is visible, and
implementation-specific variables are conventionally preceded with one
or two underscores.

Sequences
=========

Lists are a lot like JavaScript arrays:

    >>> mylist = ["hello", "there"]

Iterating through them is easy:

    >>> for i in mylist:
    ...     print i
    hello
    there

Strings are just sequences of characters, so they can be used
similarly:

    >>> for c in "boof":
    ...     print c
    b
    o
    o
    f

Tuples are just like lists, only they're immutable and differentiated
from lists by using parentheses instead of brackets:

    >>> mytuple = ("hello", "there")
    >>> mytuple[0] = "bye"
    Traceback (most recent call last):
    ...
    TypeError: 'tuple' object does not support item assignment

Tuples with a single item look a little weird, though:

    >>> mytuple = ("hello",)   # Without the comma, it'd just be a string.

It's also not possible for there to be "holes" in Python lists like
there are in Javascript arrays:

    >>> a = [1, 2, 3]
    >>> del a[1]               # Deletes '2'
    >>> a
    [1, 3]

Control Flow
============

You've already seen examples of ``for``, ``if``, and ``if...else``.
Python also supports ``if...elif``:

    >>> if 1 == 2:
    ...     pass
    ... elif 1 == 1:
    ...     print "Hooray!"
    ... else:
    ...     print "Boo."
    Hooray!

It also supports ``while``:

    >>> while False:
    ...     print "This should never display."

However, Python does not have a ``do...while`` loop.

To loop through a range of numbers, you can use the ``range()``
built-in function, which returns a list of numbers in the range you
specify:

    >>> for i in range(3):
    ...     print i
    0
    1
    2

Indexing and Slicing
====================

Any item that is a sequence can be indexed as expected, but unlike
Javascript, negative indexes may be used to denote items from the end
of the sequence:

    >>> ["hello", "there", "dude"][-1]
    'dude'

Any indexable item can generally also be sliced; this is similar to
``String.slice`` in JavaScript, only built-in to the language:

    >>> "hello"[2:4]     # Just like "hello".slice(2,4) in JS
    'll'

    >>> "hello"[2:]      # Just like "hello".slice(2) in JS
    'llo'

    >>> "hello"[:4]      # Just like "hello".slice(0,4) in JS
    'hell'

    >>> [1, 2, 3][1:2]   # Works on lists, too!
    [2]

If the datatype is mutable, you can even assign to slices:

    >>> a = [1, 2, 3, 4]
    >>> a[1:3] = [5]
    >>> a
    [1, 5, 4]

Dictionaries
============

Dictionaries are a bit like Object literals in JavaScript:

    >>> d = {"foo" : 1, "bar" : 2}
    >>> d["foo"]
    1

Their properties can't be referenced using dot notation, though:

    >>> d.foo
    Traceback (most recent call last):
    ...
    AttributeError: 'dict' object has no attribute 'foo'

Dictionaries generally aren't used to create arbitrary objects like
they are in Javascript; they don't have prototypes, nor do they have
meta-methods.  Instead, classes are used to do that sort of thing.

Classes
=======

Classes are pretty straightforward:

    >>> class Foo(object):
    ...     def __init__(self, a):
    ...         self.a = a
    ...         print "Foo created."
    ...     def doThing(self):
    ...         return self.a + 1

Here ``Foo`` is a subclass of ``object``, which is the root object
class that any class should ultimately descend from.  The constructor
is always called ``__init__()`` and is invoked like so:

    >>> f = Foo(1)
    Foo created.

So you don't need to use a ``new`` operator or anything as is the case
with JS.  Calling methods and accessing attributes is straightforward
too:

    >>> f.a
    1
    >>> f.doThing()
    2

Classes in Python get inheritance for free, but because they're not
really prototype-based, it's not easy to dynamically add or remove
methods to existing objects on-the-fly.

An object's methods are also bound to the object itself once it's
created; that is, the ``self`` parameter that's passed to them is
always the same, unlike the ``this`` parameter in JavaScript which
changes based on the object the function is attached to:

    >>> f = Foo(5)
    Foo created.
    >>> doThing = f.doThing
    >>> doThing()
    6

Operator Overloading, Special Methods, and Properties
=====================================================

Classes can define methods with special names to do all sorts of
dynamic things, from operator overloading to custom properties and
more.  Some of these dynamic features are available in JavaScript, and
some aren't.  You can read about tehm more in the Python Reference
Manual's section on `special method names`_.

.. _`special method names`: http://docs.python.org/ref/specialnames.html

Exceptions
==========

They work as expected, and there's a number of `built-in ones`_.

Python prefers the term ``raise`` to JavaScript's ``throw``, and
``except`` to JavaScript's ``catch``.  Given this, the following
code is fairly self-explanatory:

    >>> try:
    ...     raise Exception("Oof")
    ... except Exception, e:
    ...     print "Caught an exception: %s" % e
    Caught an exception: Oof

.. _`built-in ones`: http://docs.python.org/lib/module-exceptions.html

Coding Style
============

Python has a coding convention that's generally been embraced
throughout the community; almost all libraries use it.  It's contained
in `PEP 8`_.

.. _`PEP 8`: http://www.python.org/dev/peps/pep-0008

Documentation Testing
=====================

One of the most useful features of Python is one of its standard
library modules.  The `doctest`_ module allows you to test
interactive interpreter excerpts embedded either in the docstrings of
your Python code or separate files to verify their correctness.  It's
an excellent way to turn your documentation into your unit tests, and
it's also how the document you're reading right now is tested for
accuracy.

.. _`doctest`: http://docs.python.org/lib/module-doctest.html

TODOs
=====

TODO: Mention TinyPy.

TODO: Link to instructions on how to make your own modules, packages,
etc.

TODO: Mention generators, generator comprehensions/expressions, array
comprehensions, other stuff lifted from Python by JS.