General introduction

Good references to Python: ref

 
 

Set up

Virtual environments

Create a new one with:

python virtualenv/virtualenv.py venv_name

Control it with:

source venv_name/bin/activate
source venv_name/bin/deactivate

There is a new way to do this with the venv module in Python 3.

python -m venv venv_name
source venv_name/bin/activate
python-pip-logo.png

Package management

Use pip (aka the cheese shop).

pip install --upgrade flask
pip freeze >requirements.txt
pip install -r requirements.txt
pip list --outdated
pip uninstall flask

Basics

Basics

Example:

brush:python
import re
import sys

def replace(s):
  return re.replace(s,"a","b")

if __name__ = "main":
  print(replace(__name__)
  sys.exit(0)

Main defines

brush:python
print(__name__) # Name of module
print(__file__) # Location of this file

Text

brush:python
a = "hello".title() # Title case
b = "WoRlD".lower() # To lower

Classes

A class has the form:

brush:python
Class Dog:
  def __init__(self, name):
    self.name = name

  def __repr__(self):
    return "dog({})".format(self.name)

  # Optional, otherwise __repr__ is called
  def __str__(self):
    return "I am a dog. My name is {}".format(self.name)

You can define member functions and variables.

You can inherit from other classes:

brush:python
Class Akita(Dog):
  def __init__(self):
    self.name = "Akita"
 

List comprehensions

Creates a new list based on a certain condition:

brush:python
data = [1,2,3,4,5,6]
print [x for x in data]
print [x for x in data if x >3]
print [x*x for x in data]

Loops

brush:python
for I in aList:
  print(I)

Use xrange (iterator) instead of range (generator):

brush:python
for n in xrange(1,10000):
  print(n)

You can make your own iterators.

 

Advanced

Exceptions

Define an exception class:

brush:python
class MyException(Exception):
  def __init__(self, msg):
    self.msg = msg

And call it from the code:

brush:python
def liveDangerously():
  raise MyException("It's dead, Jim")

Detect the exception from the code and act accordingly:

brush:python
try:
  liveDangerously()
except MyException as exc:
  print(exc.msg)

Lambda functions

The following example sort list alist by the foo attribute of each alist instance:

brush:python
alist.sort(key=lambda x: x.foo)

Assigning a function to a variable:

brush:python
f = lambda x, y : x + y
f(1,2)

The map function:

brush:python
Celsius = [39.2, 36.5, 37.3, 37.8]
Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius)

With several lists:

brush:python
a = [1,2,3,4]
b = [17,12,11,10]
map(lambda x,y:x+y, a,b)

Reduce:

brush:python
reduce(lambda x,y: x+y, [47,11,42,13])

f = lambda a,b: a if (a > b) else b
reduce(f, [47,11,42,102,13])

References:

Run-time introspection

brush:python
instance.attribute_name
# Or
getattr(instance, attribute_name)

Override for those undefined attributes only. Catches accesses to undefined attributes:

brush:python
class Foo(object):
  def __init__(self, a):
      self.a = 1

  def __getattr__(self, attr):
      return "Help!"

This one catches all accesses to attributes. Overriding getattribute can lead to infinite recursion.

brush:python
class Foo(object):
  def __init__(self, a):
      self.a = 1

  def __getattribute__(self, attr):
      return super(Foo, self).__getattribute__(attr)

Set attribute

brush:python
__setattribute__

More info

 

Text formatting

Text formatting

Old style:

brush:python
print("Hello: %s"%("Bob"))

New style:

brush:python
print("Hello: {}".format("Bob"))

Look here for a complete comparison.

 

Best practices

Module structure

A Python modules has the following components:

README.rst
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.rst
tests/test_basic.py
tests/test_advanced.py

Example Makefile

init:
  pip install -r requirements.txt

test:
  py.test tests

.PHONY: init test

Links:

Documenting the code

brush:python
def square_and_rooter(x):
   """Return the square root of self times self."""
   ...

Use Sphynx to process documentation.

 

Logging

The Logging module simplifies log creation.

Each module should start with the following boilerplate code:

brush:python
import logging

# Optional configuration
logging.basicConfig(
    level = logging.DEBUG, 
    filename = "my.log",
    filemode='w',
    format='%(asctime)s:%(levelname)s:%(message)s')
log = logging.getLogger(__name__)
# log.setFormatter()

log.setLevel(logging.DEBUG)

Other loggers can be customised in main program, after they are loaded:

brush:python
logging.getLogger("noisyModule").setLevel(logging.CRITICAL)

Log messages can be created with different levels:

brush:python
log.debug("Preparing bases")
log.info("Print number %d", 3)
log.warning("I don't like this number")
log.error("The number is not right")
log.critical("System failure!")
log.log(10, "Custom log level")

Also from an exception:

brush:python
try:
  # raise exception
except Exception as e:
  log.exception("Exception %s", e)

Log levels can be one of:

  • 10: logging.DEBUG
  • 20: logging.INFO
  • 30: logging.WARNING
  • 40: logging.ERROR
  • 50: logging.CRITICAL

Messages can contain any of:

  • %s
  • %d
  • %f
  • %i

We can also forward logs to a file:

brush:python
filelog = logging.FileHandler(filename)
filelog.setFormatter(logging.formatter("$(asctime)s [%(threadname)16s][%(module)14s][%(levelname)8s] %(message)s"))
logging.getLogger('').addHandler(filelog)
 

Testing

Testing with doctest

The doctest module allows a module to contains its own tests in the form of commands run in an interactive shell:

brush:python
Class Dog():
  '''
     >>> r= Dog("Yeti")
     >>> r.name
     'Yeti'
     '''
  def __init__(self, name):
    self.name = name

The following boilerplate code runs the tests when the module is called directly:

brush:python
if __name__ == "__main__":
  import doctest
  doctest.testmod()

Testing with unittest

brush:python
import unittest

class DogTest(unittest.TestCase):

  def setUp(self):
      return

  def tearDown(self):
      return

  def testCalculation(self):
      self.assertEqual(fib(0), 0)
      self.assertEqual(fib(1), 1)
      self.assertEqual(fib(5), 5)
      self.assertEqual(fib(10), 55)
      self.assertEqual(fib(20), 6765)

if __name__ == "__main__":
  unittest.main()

Test are run from the command line:

cd project_directory
python -m unittest discover
 

Argument parsing

First we create an argument parser:

brush:python
from argparse import ArgumentParser
arg_parser = ArgumentParser(description='My program description')
arg_parser.add_argument('name', metavar='name',
                        help='name of the file')
arg_parser.add_argument('-v', "--verbose", action='store_true', dest='index',
                            help='verbose')

Arguments can be:

  • positional vs. -option ("name" vs. "-v" above)

Argument properties:

  • required
  • type=int
  • action="store_true"
  • action="count"
  • choices = [0, 1, 2]
  • default = 0

Then we parse the arguments

brush:python
args = arg_parser.parse_args()

And then we can access the arguments via the parser object as properties:

brush:python
# Access arguments with args
if args.index:
  print("Verbose mode enabled")
 
ast.png

Parsing

PyParsing

brush:python
import pyparsing as pp

We create rules for typical syntact tokens

brush:python
# Basic tokenizer
COMMA = pp.Literal(",").suppress()
LB    = pp.Literal("{").suppress()
RB    = pp.Literal("}").suppress()

And also for more complex tokens:

brush:python
INTEGER = pp.Word()
IDENT   = pp.Word()

Finally, we create the grammar:

brush:python
op = INTEGER | IDENT

And we parse the input:

brush:python
r = parser.parseFile(filename, parseAll = True)
print r

PyParsing pattern

An example can be found here. I prefer to separate the parsing from the class, to keep separation of concerns:

brush:python
import pyparsing as pp

class Number(oject):
  def __init__(self, num):
    self.value = num
  def __repr__(self):
    return str(self.value)

class Expression:
  def __init__(self, left, op, right):
    self.left = left
    self.op = op
    self.right = right
  def __repr__(self):
    return "({} {} {})".format(self.left, self.op, self.right)

class ExpressionParser:
  def __init__(self):
    self.parser = self.grammar()
  def grammar(self):
    num = INTEGER.setParseResults(self.parseNum)
    expr = pp.Group(INTEGER + PLUS + INTEGER).setParseResults(self.parseExpr)
  def parseNum(self, tokens):
    return Number(int(tokens[0]))
  def parseExpr(self, tokens):
    (l, op, r) = tokens[0]

    return Expression(l, op, r)

Parsing errors can be reported:

brush:python
try:
  parser.parseString(t)
except ParseException as pe:
  print(pp.line(pe.loc, t))
  print(' '*(pp.col(pe.loc,t) + '^')

We can also dump the data:

brush:python
d = parser.parseString(t)
print(d.dump())
 

NumPy

Some examples

brush:python
import numpy as np

a = np.array([2,3,4])