diff --git a/examples/python/README.fix_python_integrate b/examples/python/README.fix_python_integrate
new file mode 100644
index 0000000000000000000000000000000000000000..63de0910f44804a02e1f34ca78e0735d2a45d826
--- /dev/null
+++ b/examples/python/README.fix_python_integrate
@@ -0,0 +1,15 @@
+This folder contains several LAMMPS input scripts and a python module
+file py_integrate.py to demonstrate the use of the fix style python/integrate.
+
+in.fix_python_integrate_melt:
+This is a version of the melt example which replaces the default NVE integrator
+with a Python implementation. Fix python/integrate is used to create an
+instance of the py_integrate.NVE class which implements the required interface.
+It demonstrates how to access LAMMPS data as numpy arrays. This gives direct
+access to memory owned by the C++ code, allows easy manipulation through numpy
+operations and avoids unnecessary copies.
+
+in.fix_python_integrate_melt_opt:
+This version of melt example uses NVE_Opt instead of NVE. While this Python
+implementation is still much slower than the native version, it shows that
+simple code transformations can lead to speedups.
diff --git a/examples/python/in.fix_python_integrate_melt b/examples/python/in.fix_python_integrate_melt
new file mode 100644
index 0000000000000000000000000000000000000000..d1103f866aee73a93f5caab8afc19df64af038d0
--- /dev/null
+++ b/examples/python/in.fix_python_integrate_melt
@@ -0,0 +1,23 @@
+# 3d Lennard-Jones melt
+
+units		lj
+atom_style	atomic
+
+lattice		fcc 0.8442
+region		box block 0 10 0 10 0 10
+create_box	1 box
+create_atoms	1 box
+mass		* 1.0
+
+velocity	all create 3.0 87287
+
+pair_style	lj/cut 2.5
+pair_coeff	1 1 1.0 1.0 2.5
+
+neighbor	0.3 bin
+neigh_modify	every 20 delay 0 check no
+
+fix		1 all python/integrate py_integrate.NVE
+
+thermo		50
+run		250
diff --git a/examples/python/in.fix_python_integrate_melt_opt b/examples/python/in.fix_python_integrate_melt_opt
new file mode 100644
index 0000000000000000000000000000000000000000..fd1d11b292c4eabf33136191e5b46cc1656bc2de
--- /dev/null
+++ b/examples/python/in.fix_python_integrate_melt_opt
@@ -0,0 +1,23 @@
+# 3d Lennard-Jones melt
+
+units		lj
+atom_style	atomic
+
+lattice		fcc 0.8442
+region		box block 0 10 0 10 0 10
+create_box	1 box
+create_atoms	1 box
+mass		* 1.0
+
+velocity	all create 3.0 87287
+
+pair_style	lj/cut 2.5
+pair_coeff	1 1 1.0 1.0 2.5
+
+neighbor	0.3 bin
+neigh_modify	every 20 delay 0 check no
+
+fix		1 all python/integrate py_integrate.NVE_Opt
+
+thermo		50
+run		250
diff --git a/examples/python/py_integrate.py b/examples/python/py_integrate.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca45c0bcd0a9e847d6857530445aa024af085bc8
--- /dev/null
+++ b/examples/python/py_integrate.py
@@ -0,0 +1,110 @@
+from __future__ import print_function
+import lammps
+import ctypes
+import traceback
+import numpy as np
+
+class LAMMPSIntegrator(object):
+    def __init__(self, ptr):
+        self.lmp = lammps.lammps(ptr=ptr)
+
+    def init(self):
+        pass
+
+    def initial_integrate(self, vflag):
+        pass
+
+    def final_integrate(self):
+        pass
+
+    def initial_integrate_respa(self, vflag, ilevel, iloop):
+        pass
+
+    def final_integrate_respa(self, ilevel, iloop):
+        pass
+
+    def reset_dt(self):
+        pass
+
+
+class NVE(LAMMPSIntegrator):
+    """ Python implementation of fix/nve """
+    def __init__(self, ptr):
+        super(NVE, self).__init__(ptr)
+
+    def init(self):
+        dt = self.lmp.extract_global("dt", 1)
+        ftm2v = self.lmp.extract_global("ftm2v", 1)
+        self.ntypes = self.lmp.extract_global("ntypes", 0)
+        self.dtv = dt
+        self.dtf = 0.5 * dt * ftm2v
+
+    def initial_integrate(self, vflag):
+        nlocal = self.lmp.extract_global("nlocal", 0)
+        mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1)
+        atype = self.lmp.numpy.extract_atom_iarray("type", nlocal)
+        x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3)
+        v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3)
+        f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
+
+        for i in range(x.shape[0]):
+            dtfm = self.dtf / mass[int(atype[i])]
+            v[i,:]+= dtfm * f[i,:]
+            x[i,:] += self.dtv * v[i,:]
+
+    def final_integrate(self):
+        nlocal = self.lmp.extract_global("nlocal", 0)
+        mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1)
+        atype = self.lmp.numpy.extract_atom_iarray("type", nlocal)
+        v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3)
+        f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
+
+        for i in range(v.shape[0]):
+            dtfm = self.dtf / mass[int(atype[i])]
+            v[i,:] += dtfm * f[i,:]
+
+
+class NVE_Opt(LAMMPSIntegrator):
+    """ Tuned Python implementation of fix/nve """
+    def __init__(self, ptr):
+        super(NVE_Opt, self).__init__(ptr)
+
+    def init(self):
+        dt = self.lmp.extract_global("dt", 1)
+        ftm2v = self.lmp.extract_global("ftm2v", 1)
+        self.ntypes = self.lmp.extract_global("ntypes", 0)
+        self.dtv = dt
+        self.dtf = 0.5 * dt * ftm2v
+        self.mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1)
+
+    def initial_integrate(self, vflag):        
+        nlocal = self.lmp.extract_global("nlocal", 0)
+        atype = self.lmp.numpy.extract_atom_iarray("type", nlocal)
+        x = self.lmp.numpy.extract_atom_darray("x", nlocal, dim=3)
+        v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3)
+        f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
+        dtf = self.dtf
+        dtv = self.dtv
+        mass = self.mass
+
+        dtfm = dtf / np.take(mass, atype)
+
+        for i in range(x.shape[0]):
+            vi = v[i,:] 
+            vi += dtfm[i] * f[i,:]
+            x[i,:] += dtv * vi
+
+    def final_integrate(self):
+        nlocal = self.lmp.extract_global("nlocal", 0)
+        mass = self.lmp.numpy.extract_atom_darray("mass", self.ntypes+1)
+        atype = self.lmp.numpy.extract_atom_iarray("type", nlocal)
+        v = self.lmp.numpy.extract_atom_darray("v", nlocal, dim=3)
+        f = self.lmp.numpy.extract_atom_darray("f", nlocal, dim=3)
+        dtf = self.dtf
+        dtv = self.dtv
+        mass = self.mass
+
+        dtfm = dtf / np.take(mass, atype)
+
+        for i in range(v.shape[0]):
+            v[i,:] += dtfm[i] * f[i,:]
diff --git a/src/PYTHON/fix_python_integrate.cpp b/src/PYTHON/fix_python_integrate.cpp
index a9c3116ed4cfdb6e02b0ae04a2c52335267f31ae..00f5a5544d71e2d037bb192d333842d44aa0acc5 100644
--- a/src/PYTHON/fix_python_integrate.cpp
+++ b/src/PYTHON/fix_python_integrate.cpp
@@ -52,11 +52,11 @@ FixPythonIntegrate::FixPythonIntegrate(LAMMPS *lmp, int narg, char **arg) :
 
 
   // create integrator instance
-  char * full_cls_name = arg[2];
+  char * full_cls_name = arg[3];
   char * lastpos = strrchr(full_cls_name, '.');
 
   if (lastpos == NULL) {
-    error->all(FLERR,"Python pair style requires fully qualified class name");
+    error->all(FLERR,"Fix python/integrate requires fully qualified class name");
   }
 
   size_t module_name_length = strlen(full_cls_name) - strlen(lastpos);
@@ -91,7 +91,11 @@ FixPythonIntegrate::FixPythonIntegrate(LAMMPS *lmp, int narg, char **arg) :
   delete [] module_name;
   delete [] cls_name;
 
-  PyObject * py_integrator_obj = PyObject_CallObject(py_integrator_type, NULL);
+  PyObject * ptr = PY_VOID_POINTER(lmp);
+  PyObject * arglist = Py_BuildValue("(O)", ptr);
+  PyObject * py_integrator_obj = PyObject_CallObject(py_integrator_type, arglist);
+  Py_DECREF(arglist);
+
   if (!py_integrator_obj) {
     PyErr_Print();
     PyErr_Clear();
@@ -139,10 +143,7 @@ void FixPythonIntegrate::init()
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'init' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(O)", ptr);
-  PyObject * result = PyEval_CallObject(py_init, arglist);
-  Py_DECREF(arglist);
+  PyObject * result = PyEval_CallObject(py_init, NULL);
   PyGILState_Release(gstate);
 }
 
@@ -159,8 +160,7 @@ void FixPythonIntegrate::initial_integrate(int vflag)
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'initial_integrate' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(Oi)", ptr, vflag);
+  PyObject * arglist = Py_BuildValue("(i)", vflag);
   PyObject * result = PyEval_CallObject(py_initial_integrate, arglist);
   Py_DECREF(arglist);
   PyGILState_Release(gstate);
@@ -179,10 +179,7 @@ void FixPythonIntegrate::final_integrate()
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'final_integrate' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(O)", ptr);
-  PyObject * result = PyEval_CallObject(py_final_integrate, arglist);
-  Py_DECREF(arglist);
+  PyObject * result = PyEval_CallObject(py_final_integrate, NULL);
   PyGILState_Release(gstate);
 }
 
@@ -199,8 +196,7 @@ void FixPythonIntegrate::initial_integrate_respa(int vflag, int ilevel, int iloo
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'initial_integrate_respa' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(Oiii)", ptr, vflag, ilevel, iloop);
+  PyObject * arglist = Py_BuildValue("(iii)", vflag, ilevel, iloop);
   PyObject * result = PyEval_CallObject(py_initial_integrate_respa, arglist);
   Py_DECREF(arglist);
   PyGILState_Release(gstate);
@@ -219,8 +215,7 @@ void FixPythonIntegrate::final_integrate_respa(int ilevel, int iloop)
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'final_integrate_respa' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(Oii)", ptr, ilevel, iloop);
+  PyObject * arglist = Py_BuildValue("(ii)", ilevel, iloop);
   PyObject * result = PyEval_CallObject(py_final_integrate_respa, arglist);
   Py_DECREF(arglist);
   PyGILState_Release(gstate);
@@ -239,9 +234,6 @@ void FixPythonIntegrate::reset_dt()
     PyGILState_Release(gstate);
     error->all(FLERR,"Could not find 'reset_dt' method'");
   }
-  PyObject * ptr = PY_VOID_POINTER(lmp);
-  PyObject * arglist = Py_BuildValue("(O)", ptr);
-  PyObject * result = PyEval_CallObject(py_reset_dt, arglist);
-  Py_DECREF(arglist);
+  PyObject * result = PyEval_CallObject(py_reset_dt, NULL);
   PyGILState_Release(gstate);
 }