Saturday, September 23, 2006

Python Bookmarks, Tricks, Tips, Gotchas

Python = Best Language EVAR! 'nuff said. Here are some Bookmarks, Tricks, Tips, and Gotchas that I've compiled:

BOOKMARKS

TRICKS

"dictionary comprehensions"
>>> dict((x, None) for x in [1,2,3])
{1: None, 2: None, 3: None}

Running a script in an interactive session
>>> import sys
>>> sys.argv = ["spam.py", "one", "two", "three"]
>>> execfile("spam.py")

Ternary if else statement (2.5 only)
x = true_value if condition else false_value

"switch" statement using dictionary
def function_1 (...):
...
functions = {'a': function_1,
'b': function_2,
'c': self.method_1, ...}
func = functions[value]
func()

list comprehension with multiple ifs/fors
>>> result = [x for x in range(10) if x % 2 == 0 if x % 3 == 0]
>>> result
[0, 6]
>>> result = [ x*y for x in range(10) if x%2 == 0 for y in range(10) if y % 3 == 0]
>>> result
[0, 0, 0, 0, 0, 6, 12, 18, 0, 12, 24, 36, 0, 18, 36, 54, 0, 24, 48, 72]

turn a list into a string and back again
>>> ' '.join(['a', 'b', 'c'])
'a b c'
>>> _.split()
['a', 'b', 'c']

more "mathy" numeric boolean comparison
>>> 4<6<8>>> 1<2<3<4<5>4>3>2>1
True

any() and all() (2.5 only)
>>> any([False, False, False])
False
>>> any([False, False, True])
True
>>> all([True, True, False])
False
>>> all([True,True,True])
True

switch values without a temporary variable
>>> a,b = b,a

Drop into a python debugging session when you hit this line
import pdb ; pdb.set_trace()

Zen of Python easter egg:
>>> import this

Reverse a string:
>>> 'string'[::-1]
'gnirts'

Quicksort in three lines of code (pure propaganda piece, don't use in real code... from Python Cookbook, p.215)
def qsort(L):
if len(L) &lt;= 1: return L
return qsort([lt for lt in L[1:] if lt &lt; L[0]]) + L[0:1] + qsort([ge for ge in L[1:] if ge >= L[0]])

Create a 3-D array initialized to 1(when working with large arrays, always use Numeric)
import Numeric as N
a = N.empty(2*3*4)
for i, x in enumerate(a): a[i] = 1
a = N.reshape(a, (2,3,4))

Enumerate and zip at the same time
for (i,x),y,z in zip(enumerate(a),b,c): print i,x,y,z

TIPS (code samples taken from Dive Into Python)
  • use k in d instead of k in d.keys() for checking for a key in a dictionary (the former is a built-in call to C code and a O(1) operation rather than a Python module with O(n)
  • enumerate(seq) returns index/value pair for a sequence: use it in for loops when you need both values (NEVER use len(range(seq)))
  • Introspection: dir(obj) returns a list of all attributes of obj; vars(obj) returns a dictionary of the same (incl. the values of those attributes); don't forget about locals() and globals(), either
  • isinstance(obj, class) tells whether obj is an instance of class... use it instead of if type(X) == type(Y)
  • callable(obj) returns boolean indicating weather the object is callable or not
  • getattr(x, 'y', default) is the reflection-y way of doing x.y
  • Assign multiple values at once: x , y, z = 1, 'blah', function
  • Python supports filename globbing: use the glob function in the glob module
  • Regular expressions: always use raw strings to avoid excessive backslashing
    • re.sub(r'\bROAD$', 'RD', stringToModify) substitution
    • re.compile for more complicated/reoccuring tasks
      • phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$')
      • phonePattern.search('800-555-1212').groups()
    • re.search(r'[^aeiou]y$', 'vacancy') search
  • You can pass each item of a sequence (instead of the entire sequence itself) into a function that supports it (i.e. defined such as def fn(*x)) by calling it: fn(*seq) If seq is a dict, the function call passes in each key.
  • Change a bunch of non-object variables by passing them into a function and returning them all in a tuple
  • Use the timeit module for small code snippets
  • If you want "true" division, (5/3 = 1.67 instead of 1) use 5//3 or add from __future__ import division to the beginning of your module
  • Use sys.stdout.write() instead of print to avoid trailing characters when writing to stdout
  • You can create a dictionary that acts like a list by using integers (0...n) as keys
GOTCHAS
  • Python one-liners are tricky (usually in form python -c "code"... see this thread
  • __init__ doesn't "construct" something... the object has already been constructed by the time the interpreter hits __init__
  • Keys of dictionaries must be hashable... that means lists as keys are right out
  • EVERYTHING in python is an object (try it at the interpreter if you don't believe me)
  • self should always be the first argument for every function in a class. You must bind all passed in variables (self.value = value) to make them attributes of the class (preferably in __init__). Refer to class attributes within a class as self.value.
  • When you add a value pair to a dictionary whose key already exists in the dictionary, the new value pair replaces the old one
  • Adding __ to a class attribute (e.g. __attributeX) makes it module-private
  • Don't use optional arguments to initialize mutable objects! Any optional arguments (e.g. def f(x, y=[])) get assigned during evaluation, not execution. Use this form instead: def(x, y=None): if y is None: y = []
  • Python raises an exception when __init__ returns anything other than None
  • a = b and a = copy.copy(b) both assign a to the same object as b (shallow copy). For a deep copy, use copy.deepcopy
  • os.walk doesn't change the current directory, so using something like os.path.abspath(filename) will NOT work... use os.path.join(top, dirpath, filename) instead
  • comparing a string to a integer works! (when operations do not!)
  • If you get a RuntimeError: dictionary changed size during iteration, try changing for x in y to for x in y.keys()
  • More gotchas: 10 Python Pitfalls, Python Gotchas, When Pythons Attack

No comments: