Excellent answer, but none points out what I think is one key insight for programmers coming to Python with background in other languages such as Java or C++: import, def and class are not "instructions to the compiler", "declarations", or other kind of magical incantations: they're executable statements like any other. For example, the def statement:
def f(x): return x + 23
is almost exactly equivalent to the assignment statement:
f = lambda x: x + 23
(stylistically the def is preferable as it makes f.__name__ meaningful -- that's the "almost" part; lambda is rather limited and should only ever be used when you're really keen to make an anonymous function rather than a normal named one). Similarly,
class X(object): zap = 23
is equivalent to the assignment:
X = type('X', (), {'zap': 23})
(again, stylistically, class is preferable, afford more generality, like def it allows decoration, etc, etc; the point I'm making is that there is semantic equivalence here).
So, when you run a .py file, or import it for the first time in a program's run, Python executes its top-level statements one after the other -- in normal good Python style, most will be assignments, def, class, or import, but at least one will be a call (normally to a function) to execute that function's body of code (def, like lambda, just compiles that code; the compiled code object only executes when the function or lambda is called). Other answers have already suggested practical considerations such as testing __name__ in order to make a module that can either be run directly or imported, etc.
Finally, it's best to have all "significant" code in functions (or methods in classes), not just stylistically, but because code in a function executes significantly faster (since the Python compiler can then automatically optimize all accesses to local variables). For example, consider...:
import time
lotsotimes = range(1000*1000)
start = time.time()
for x in lotsotimes:
x = x + x
stend = time.time()
print 'in module toplev: %.6f' % (stend - start)
def fun():
start = time.time()
for x in lotsotimes:
x = x + x
stend = time.time()
print 'in function body: %.6f' % (stend - start)
fun()
On my laptop, with Python 2.6, this emits:
in module toplev: 0.405440
in function body: 0.123296
So, for code that does a lot of variable accesses and little else, running it in a function as opposed to running it as module top-level code could speed it up by more than 3 times.
The detailed explanation: at module-level, all variables are inevitably kept in a dictionary, so each variable access is a dict-access; local variables of a function get optimized into a special array, so access is faster (the difference is even more extreme than the 20% or so speed-up you'd see by accessing an item in a Python list vs one in a Python dict, since the local-variable optimization also saves hashing & other ancillary costs).