Python pitfalls

Take care that python manage negative indices : the values start then from the end of the table

In []:
a = [1,2,3,4,5]
print a[0]
print a[-1]
print a[-2]
Generating a double entry table : Warning, when doing :
In []:
def pprint(x):
  tab = str(x).split('],')
  for elem in tab:
    print elem
  print 
  return  

a=[[1,2,3,4,5]]*4

pprint(a)

a[0][0] = 9

pprint(a)
Better do :
In []:
import numpy as np

a = np.array([[1,2,3,4,5]]*4)
print "a:",a,"\n"

a[0][0] = 9

print "a:",a

Solution without using np : less intuitive : the loop generate different objects taking same array as value

In []:
a = [[1,2,3,4,5] for _ in range(4)]

pprint(a)

a[0][0] = 9

pprint(a)

copy (references and assignement)

In []:
a = [1,2,3]

b = a

b[1] = 7

print 'a :', a, '\nb :', b, ', a is b:',a is b
In []:
import copy

a = [1,2,3]

b = copy.deepcopy(a)

b[1] = 8

print 'a :', a, '\nb :', b, ', a is b:',a is b

Variables passed by reference as arrays and dictionaries modified in subroutine keep modified in main (mutable type)

In []:
def ff(x):
  for i in range(0,len(x)):
     ss=x[i]*x[i] 
     x[i]=ss
  print '  x in ff:',x,"\n"      
  return  
# -------------------------

xtab = [0,1,2,3]

print 'xtab:',xtab

ff(xtab)

print 'xtab after ff:',xtab
In []:
def ff(x):
  for xk in x.keys():
    x[xk] += "!!"
  return

xdict = { 'a' : 'aa', 'b' : 'bb', 'c' : 'cc'}

ff(xdict)

print xdict

variable scope (global, local)

In []:
def f(x):
  global y
  z = 4  
  y = 2  
  return x + y

y = 3

z = 5

print 'y:',y,'z:',z

x = 3

print f(x)

print 'y:',y,'z:',z,'--> z is not modified, as y is impacted by call to f(x)'

import collisions :

Take care when using from import *

In []:
%reset -f
In []:
start = -1
print sum(range(5), start)
In []:
from numpy import *
print sum(range(5), start)  # using sum from numpy which works differently than native sum function

Better write :

In []:
import numpy as np 
print np.sum(range(5), start)
Using "from import *" does not help for managing dependencies of python modules

implicit type conversion (duck typing)

In []:
print 4/3
In []:
a = 1
print 'type(',a,'):',type(a)
a = 1 + 4.5
print 'type(',a,'):',type(a)

integer division (1/2=0 in py 2.x 1/2=0.5 in py3 and 1//2=0 in py3)

In []:
print 1/2

from __future__ import division
print (1/2)
print (1//2)

Octal numbers start with 0 !, 010+07 = 15 (0x for hex, 0b for bin)

In []:
 print '10 + 7   :',10+7
 print '010 + 07 :',010+07,"In octal: {0:o}".format(010+07),"or {0:#o}".format(010+07)    
 print 'x10 + 7  :',0x10+7,"In hexadecimal: {0:x}".format(0x10+7),"or {0:#x}".format(0x10+7)   
 print '0b10 + 7 :',0b10+7,"In binary: {0:b}".format(0b10+7),"or {0:#b}".format(0b10+7) 

As a consequence:

In []:
print 09

But numbers with component less than 8 are valid :

In []:
print 01,07

runtime errors are not caught until function executes

In []:
a = 5
if a == 4:
  c = a + b
 
if a == 5:
  c = a + b
    
In []:
def divisionError():
  if a==5:
    c = a / 0
  return

if a==4:
  print "First call:"
  divisionError() 
    
if a==5:
  print "Second call:"
  divisionError()