Metaclass conflict when importing discretize
**What was done**
1. Using a new python 3.7.3 virtualenv, run `pip install discretize`
2. Start the python interpreter
3. run `import discretize`

**What was seen**
```
>>> import discretize
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/ianmacaulay/tmp/venv3/lib/python3.7/site-packages/discretize/__init__.py", line 2, in <module>
    from discretize.TensorMesh import TensorMesh
  File "/Users/ianmacaulay/tmp/venv3/lib/python3.7/site-packages/discretize/TensorMesh.py", line 15, in <module>
    InnerProducts, TensorMeshIO
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
```

**What was expected**
Discretize should import properly.

OS: MacOS 10.14.6
python: 3.7.3
Version: 0.4.9

The only version that I was able to get to work was 0.4.4 on a clean install, although I was required to install numpy myself before I could install discretize
diff --git a/discretize/TreeMesh.py b/discretize/TreeMesh.py
index 367681ec..7226a32c 100644
--- a/discretize/TreeMesh.py
+++ b/discretize/TreeMesh.py
@@ -97,6 +97,15 @@ from scipy.spatial import Delaunay
 import scipy.sparse as sp
 from six import integer_types
 
+from discretize.utils.codeutils import requires
+# matplotlib is a soft dependencies for discretize
+try:
+    import matplotlib.pyplot as plt
+    import matplotlib
+except ImportError:
+    matplotlib = False
+
+
 class TreeMesh(_TreeMesh, BaseTensorMesh, InnerProducts, TreeMeshIO):
     """
     TreeMesh is a class for adaptive QuadTree (2D) and OcTree (3D) meshes.
@@ -521,6 +530,7 @@ class TreeMesh(_TreeMesh, BaseTensorMesh, InnerProducts, TreeMeshIO):
             P = np.r_[Px, Py, Pz]
         return sp.identity(self.nE).tocsr()[P]
 
+    @requires({'matplotlib': matplotlib})
     def plotSlice(
         self, v, vType='CC',
         normal='Z', ind=None, grid=False, view='real',
@@ -563,9 +573,6 @@ class TreeMesh(_TreeMesh, BaseTensorMesh, InnerProducts, TreeMeshIO):
         if not isinstance(grid, bool):
             raise TypeError('grid must be a boolean')
 
-        import matplotlib.pyplot as plt
-        import matplotlib
-
         normalInd = {'X': 0, 'Y': 1, 'Z': 2}[normal]
         antiNormalInd = {'X': [1, 2], 'Y': [0, 2], 'Z': [0, 1]}[normal]
 
diff --git a/discretize/View.py b/discretize/View.py
index 78b72ce3..ba5d6d90 100644
--- a/discretize/View.py
+++ b/discretize/View.py
@@ -17,7 +17,6 @@ except ImportError:
     matplotlib = False
 
 
-@requires({'matplotlib': matplotlib})
 class TensorView(object):
     """Provides viewing functions for TensorMesh
 
@@ -49,6 +48,7 @@ class TensorView(object):
     #         pltNum +=1
     #     if showIt: plt.show()
 
+    @requires({'matplotlib': matplotlib})
     def plotImage(
         self, v, vType='CC', grid=False, view='real',
         ax=None, clim=None, showIt=False,
@@ -202,6 +202,7 @@ class TensorView(object):
             plt.show()
         return ph
 
+    @requires({'matplotlib': matplotlib})
     def plotSlice(
         self, v, vType='CC',
         normal='Z', ind=None, grid=False, view='real',
@@ -369,6 +370,7 @@ class TensorView(object):
         ax.set_title('Slice {0:.0f}'.format(ind))
         return out
 
+    @requires({'matplotlib': matplotlib})
     def _plotImage2D(
         self, v, vType='CC', grid=False, view='real',
         ax=None, clim=None, showIt=False,
@@ -570,6 +572,7 @@ class TensorView(object):
             plt.show()
         return out
 
+    @requires({'matplotlib': matplotlib})
     def plotGrid(
         self, ax=None, nodes=False, faces=False, centers=False, edges=False,
         lines=True, showIt=False, **kwargs
@@ -742,6 +745,7 @@ class TensorView(object):
         return ax
 
 
+    @requires({'matplotlib': matplotlib})
     def plot_3d_slicer(self, v, xslice=None, yslice=None, zslice=None,
                        vType='CC', view='real', axis='xy', transparent=None,
                        clim=None, xlim=None, ylim=None, zlim=None,
@@ -792,9 +796,12 @@ class TensorView(object):
         plt.show()
 
 
-@requires({'matplotlib': matplotlib})
 class CylView(object):
 
+    def __init__(self):
+        pass
+
+    @requires({'matplotlib': matplotlib})
     def _plotCylTensorMesh(self, plotType, *args, **kwargs):
 
         if not self.isSymmetric:
@@ -891,6 +898,7 @@ class CylView(object):
 
         return out
 
+    @requires({'matplotlib': matplotlib})
     def plotGrid(self, *args, **kwargs):
         if self.isSymmetric:
             return self._plotCylTensorMesh('plotGrid', *args, **kwargs)
@@ -960,6 +968,7 @@ for reference, see: http://matplotlib.org/examples/pylab_examples/polar_demo.htm
 
         return ax
 
+    @requires({'matplotlib': matplotlib})
     def _plotGridThetaSlice(self, *args, **kwargs):
         if self.isSymmetric:
             return self.plotGrid(*args, **kwargs)
@@ -969,6 +978,7 @@ for reference, see: http://matplotlib.org/examples/pylab_examples/polar_demo.htm
         mesh2D = self.__class__(h=h2d, x0=self.x0)
         return mesh2D.plotGrid(*args, **kwargs)
 
+    @requires({'matplotlib': matplotlib})
     def _plotGridZSlice(self, *args, **kwargs):
         # https://github.com/matplotlib/matplotlib/issues/312
         ax = kwargs.get('ax', None)
@@ -1019,11 +1029,11 @@ for reference, see: http://matplotlib.org/examples/pylab_examples/polar_demo.htm
 
         return ax
 
+    @requires({'matplotlib': matplotlib})
     def plotImage(self, *args, **kwargs):
         return self._plotCylTensorMesh('plotImage', *args, **kwargs)
 
 
-@requires({'matplotlib': matplotlib})
 class CurviView(object):
     """
     Provides viewing functions for CurvilinearMesh
@@ -1034,6 +1044,7 @@ class CurviView(object):
     def __init__(self):
         pass
 
+    @requires({'matplotlib': matplotlib})
     def plotGrid(
         self, ax=None, nodes=False, faces=False, centers=False, edges=False,
         lines=True, showIt=False, **kwargs
@@ -1131,6 +1142,7 @@ class CurviView(object):
 
         return ax
 
+    @requires({'matplotlib': matplotlib})
     def plotImage(
         self, I, ax=None, showIt=False, grid=False, clim=None
     ):
diff --git a/discretize/tree_ext.pyx b/discretize/tree_ext.pyx
index 422f6bbb..5291b82a 100644
--- a/discretize/tree_ext.pyx
+++ b/discretize/tree_ext.pyx
@@ -13,6 +13,19 @@ from scipy.spatial import Delaunay, cKDTree
 from six import integer_types
 import numpy as np
 
+from discretize.utils.codeutils import requires
+# matplotlib is a soft dependencies for discretize
+try:
+    import matplotlib
+    import matplotlib.cm as cmx
+    import matplotlib.pyplot as plt
+    import matplotlib.colors as colors
+    from mpl_toolkits.mplot3d import Axes3D
+    from matplotlib.collections import PatchCollection
+except ImportError:
+    matplotlib = False
+
+
 cdef class TreeCell:
     """A Cell of the `TreeMesh`
 
@@ -3303,6 +3316,7 @@ cdef class _TreeMesh:
 
         return sp.csr_matrix((V, (I, J)), shape=(locs.shape[0],self.nC))
 
+    @requires({'matplotlib': matplotlib})
     def plotGrid(self,
         ax=None, nodes=False, faces=False, centers=False, edges=False,
         lines=True, cell_line=False,
@@ -3345,15 +3359,10 @@ cdef class _TreeMesh:
             Axes handle for the plot
 
         """
-        import matplotlib
         if ax is None:
-            import matplotlib.pyplot as plt
-            import matplotlib.colors as colors
-            import matplotlib.cm as cmx
             if(self._dim == 2):
                 ax = plt.subplot(111)
             else:
-                from mpl_toolkits.mplot3d import Axes3D
                 ax = plt.subplot(111, projection='3d')
         else:
             if not isinstance(ax, matplotlib.axes.Axes):
@@ -3504,6 +3513,7 @@ cdef class _TreeMesh:
 
         return ax
 
+    @requires({'matplotlib': matplotlib})
     def plotImage(self, v, vType='CC', grid=False, view='real',
                   ax=None, clim=None, showIt=False,
                   pcolorOpts=None,
@@ -3545,13 +3555,6 @@ cdef class _TreeMesh:
         if view == 'vec':
             raise NotImplementedError('Vector ploting is not supported on TreeMesh (yet)')
 
-        import matplotlib.pyplot as plt
-        import matplotlib
-        import matplotlib.colors as colors
-        import matplotlib.cm as cmx
-        from matplotlib.collections import PatchCollection
-
-
         if view in ['real', 'imag', 'abs']:
             v = getattr(np, view)(v) # e.g. np.real(v)
         if vType == 'CC':
['discretize/tree_ext.pyx', 'discretize/TreeMesh.py', 'discretize/View.py']