Repository: NCAS-CMS/cfdm

Issue:
Allow `array` and `datetime_array` attributes on data-bearing contructs
Currently, the only way to access an array of a construct that has data is `f.data.array`. Experience indicates that this is needlessly verbose, and `f.array` should also work. Similarly for `datetime_array`.

Discussion:

Patch:
diff --git a/Changelog.rst b/Changelog.rst
index d171c57a6..95e4edffa 100644
--- a/Changelog.rst
+++ b/Changelog.rst
@@ -12,6 +12,8 @@ Version 1.9.1.0
 * New method: `cfdm.Field.domain_ancillary`
 * New method: `cfdm.Field.domain_axis`
 * New method: `cfdm.Field.field_ancillary`
+* New attribute: `cfdm.Field.array`
+* New attribute: `cfdm.Field.datetime_array`
 * New construct retrieval API methods
   (https://github.com/NCAS-CMS/cfdm/issues/179)
 * Implement (bar writing to netCDF files) lossy compression by
diff --git a/cfdm/mixin/propertiesdata.py b/cfdm/mixin/propertiesdata.py
index b32c1b13c..e595265c8 100644
--- a/cfdm/mixin/propertiesdata.py
+++ b/cfdm/mixin/propertiesdata.py
@@ -180,9 +180,73 @@ class PropertiesData(Properties):
         """
         print("In _test_docstring_substitution")
 
-    # ----------------------------------------------------------------
-    # Attributes
-    # ----------------------------------------------------------------
+    @property
+    def array(self):
+        """A numpy array deep copy of the data.
+
+        Changing the returned numpy array does not change the data
+        array.
+
+        .. versionadded:: 1.9.1.0
+
+        .. seealso:: `data`, `datetime_array`
+
+        **Examples**
+
+        >>> f.data
+        <{{repr}}Data(5): [0, ... 4] kg m-1 s-2>
+        >>> a = f.array
+        >>> type(a)
+        <type 'numpy.ndarray'>
+        >>> print(a)
+        [0 1 2 3 4]
+        >>> a[0] = 999
+        >>> print(a)
+        [999 1 2 3 4]
+        >>> print(f.array)
+        [0 1 2 3 4]
+        >>> f.data
+        <{{repr}}Data(5): [0, ... 4] kg m-1 s-2>
+
+        """
+        data = self.get_data(None)
+        if data is None:
+            raise AttributeError(f"{self.__class__.__name__} has no data")
+
+        return data.array
+
+    @property
+    def datetime_array(self):
+        """An independent numpy array of date-time objects.
+
+        Only applicable for data with reference time units.
+
+        If the calendar has not been set then the CF default calendar
+        will be used and the units will be updated accordingly.
+
+        .. versionadded:: 1.9.1.0
+
+        .. seealso:: `array`, `data`
+
+        **Examples**
+
+        >>> f.units
+        'days since 2000-01-01'
+        >>> print(f.array)
+        [ 0 31 60 91]
+        >>> print(f.datetime_array)
+        [cftime.DatetimeGregorian(2000-01-01 00:00:00)
+         cftime.DatetimeGregorian(2000-02-01 00:00:00)
+         cftime.DatetimeGregorian(2000-03-01 00:00:00)
+         cftime.DatetimeGregorian(2000-04-01 00:00:00)]
+
+        """
+        data = self.get_data(None)
+        if data is None:
+            raise AttributeError(f"{self.__class__.__name__} has no data")
+
+        return data.datetime_array
+
     @property
     def dtype(self):
         """Data-type of the data elements.
diff --git a/docs/source/class/cfdm.AuxiliaryCoordinate.rst b/docs/source/class/cfdm.AuxiliaryCoordinate.rst
index 2de64bf77..fdd679316 100644
--- a/docs/source/class/cfdm.AuxiliaryCoordinate.rst
+++ b/docs/source/class/cfdm.AuxiliaryCoordinate.rst
@@ -79,7 +79,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.AuxiliaryCoordinate.array
    ~cfdm.AuxiliaryCoordinate.data
+   ~cfdm.AuxiliaryCoordinate.datetime_array
    ~cfdm.AuxiliaryCoordinate.dtype
    ~cfdm.AuxiliaryCoordinate.ndim
    ~cfdm.AuxiliaryCoordinate.shape
diff --git a/docs/source/class/cfdm.Bounds.rst b/docs/source/class/cfdm.Bounds.rst
index 1523523cf..50ed40bdf 100644
--- a/docs/source/class/cfdm.Bounds.rst
+++ b/docs/source/class/cfdm.Bounds.rst
@@ -69,7 +69,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.Bounds.array
    ~cfdm.Bounds.data
+   ~cfdm.Bounds.datetime_array
    ~cfdm.Bounds.dtype
    ~cfdm.Bounds.ndim
    ~cfdm.Bounds.shape
diff --git a/docs/source/class/cfdm.CellMeasure.rst b/docs/source/class/cfdm.CellMeasure.rst
index 8f4961a6d..8ae50f04e 100644
--- a/docs/source/class/cfdm.CellMeasure.rst
+++ b/docs/source/class/cfdm.CellMeasure.rst
@@ -84,7 +84,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.CellMeasure.array
    ~cfdm.CellMeasure.data
+   ~cfdm.CellMeasure.datetime_array
    ~cfdm.CellMeasure.dtype
    ~cfdm.CellMeasure.ndim
    ~cfdm.CellMeasure.shape
diff --git a/docs/source/class/cfdm.Count.rst b/docs/source/class/cfdm.Count.rst
index 3915c48cc..2b7ffa7a7 100644
--- a/docs/source/class/cfdm.Count.rst
+++ b/docs/source/class/cfdm.Count.rst
@@ -68,7 +68,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.Count.array
    ~cfdm.Count.data
+   ~cfdm.Count.datetime_array
    ~cfdm.Count.dtype
    ~cfdm.Count.ndim
    ~cfdm.Count.shape
diff --git a/docs/source/class/cfdm.DimensionCoordinate.rst b/docs/source/class/cfdm.DimensionCoordinate.rst
index 7f0ee3714..562799a11 100644
--- a/docs/source/class/cfdm.DimensionCoordinate.rst
+++ b/docs/source/class/cfdm.DimensionCoordinate.rst
@@ -79,7 +79,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.DimensionCoordinate.array
    ~cfdm.DimensionCoordinate.data
+   ~cfdm.DimensionCoordinate.datetime_array
    ~cfdm.DimensionCoordinate.dtype
    ~cfdm.DimensionCoordinate.ndim
    ~cfdm.DimensionCoordinate.shape
diff --git a/docs/source/class/cfdm.DomainAncillary.rst b/docs/source/class/cfdm.DomainAncillary.rst
index 3305e6631..7d3172d49 100644
--- a/docs/source/class/cfdm.DomainAncillary.rst
+++ b/docs/source/class/cfdm.DomainAncillary.rst
@@ -79,7 +79,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.DomainAncillary.array
    ~cfdm.DomainAncillary.data
+   ~cfdm.DomainAncillary.datetime_array
    ~cfdm.DomainAncillary.dtype
    ~cfdm.DomainAncillary.ndim
    ~cfdm.DomainAncillary.shape
diff --git a/docs/source/class/cfdm.Field.rst b/docs/source/class/cfdm.Field.rst
index d180ad56e..d2aceccf5 100644
--- a/docs/source/class/cfdm.Field.rst
+++ b/docs/source/class/cfdm.Field.rst
@@ -89,7 +89,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.Field.array
    ~cfdm.Field.data
+   ~cfdm.Field.datetime_array
    ~cfdm.Field.dtype
    ~cfdm.Field.ndim
    ~cfdm.Field.shape
diff --git a/docs/source/class/cfdm.FieldAncillary.rst b/docs/source/class/cfdm.FieldAncillary.rst
index ff539574f..29cb73e37 100644
--- a/docs/source/class/cfdm.FieldAncillary.rst
+++ b/docs/source/class/cfdm.FieldAncillary.rst
@@ -79,7 +79,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.FieldAncillary.array
    ~cfdm.FieldAncillary.data
+   ~cfdm.FieldAncillary.datetime_array
    ~cfdm.FieldAncillary.dtype
    ~cfdm.FieldAncillary.ndim
    ~cfdm.FieldAncillary.shape
diff --git a/docs/source/class/cfdm.Index.rst b/docs/source/class/cfdm.Index.rst
index 61ef36c19..d895a2351 100644
--- a/docs/source/class/cfdm.Index.rst
+++ b/docs/source/class/cfdm.Index.rst
@@ -68,7 +68,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.Index.array
    ~cfdm.Index.data
+   ~cfdm.Index.datetime_array
    ~cfdm.Index.dtype
    ~cfdm.Index.ndim 
    ~cfdm.Index.shape
diff --git a/docs/source/class/cfdm.InteriorRing.rst b/docs/source/class/cfdm.InteriorRing.rst
index 84f4a46ec..9437d2a2f 100644
--- a/docs/source/class/cfdm.InteriorRing.rst
+++ b/docs/source/class/cfdm.InteriorRing.rst
@@ -68,7 +68,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.InteriorRing.array
    ~cfdm.InteriorRing.data
+   ~cfdm.InteriorRing.datetime_array
    ~cfdm.InteriorRing.dtype
    ~cfdm.InteriorRing.ndim
    ~cfdm.InteriorRing.shape
diff --git a/docs/source/class/cfdm.List.rst b/docs/source/class/cfdm.List.rst
index 1d7123e61..803045fee 100644
--- a/docs/source/class/cfdm.List.rst
+++ b/docs/source/class/cfdm.List.rst
@@ -68,7 +68,9 @@ Data
    :toctree: ../attribute/
    :template: attribute.rst
 
+   ~cfdm.List.array
    ~cfdm.List.data
+   ~cfdm.List.datetime_array
    ~cfdm.List.dtype
    ~cfdm.List.ndim    
    ~cfdm.List.shape
diff --git a/docs/source/tutorial.py b/docs/source/tutorial.py
index 9b05b61b4..03c139442 100644
--- a/docs/source/tutorial.py
+++ b/docs/source/tutorial.py
@@ -39,7 +39,7 @@ print(q.constructs)
 t.constructs
 print(t.constructs)
 t.data
-print(t.data.array)
+print(t.array)
 t.dtype
 t.ndim
 t.shape
@@ -77,9 +77,9 @@ print(q.data.mask.array)
 q.data.mask.any()
 cfdm.write(q, 'masked_q.nc')
 no_mask_q = cfdm.read('masked_q.nc', mask=False)[0]
-print(no_mask_q.data.array)
+print(no_mask_q.array)
 masked_q = no_mask_q.apply_masking()
-print(masked_q.data.array)
+print(masked_q.array)
 data = t.data
 data.shape
 data[:, :, 1].shape
@@ -91,13 +91,13 @@ import numpy
 t.data[:, 0, 0] = -1
 t.data[:, :, 1] = -2
 t.data[..., 6:3:-1, 3:6] = -3
-print(t.data.array)
+print(t.array)
 t.data[..., 6:3:-1, 3:6] = numpy.arange(9).reshape(3, 3)
 t.data[0, [2, 9], [4, 8]] =  cfdm.Data([[-4, -5]])
-print(t.data.array)
+print(t.array)
 t.data[0, :, -2] = cfdm.masked
 t.data[0, 5, -2] = -6
-print(t.data.array)
+print(t.array)
 print(q)
 new = q[::-1, 0]
 print(new)
@@ -196,15 +196,15 @@ lon
 lon.data
 lon.data[2]
 lon.data[2] = 133.33
-print(lon.data.array)
+print(lon.array)
 t.get_data_axes('latitude')
 t.constructs.data_axes()
 time = q.construct('time')
 time
 time.get_property('units')
 time.get_property('calendar', default='standard')
-print(time.data.array)
-print(time.data.datetime_array)
+print(time.array)
+print(time.datetime_array)
 domain = t.domain
 domain
 print(domain)
@@ -226,7 +226,7 @@ lon = t.coordinate('grid_longitude')
 bounds = lon.bounds
 bounds
 bounds.data
-print(bounds.data.array)
+print(bounds.array)
 bounds.inherited_properties()
 bounds.properties()
 f = cfdm.read('geometry.nc')[0]
@@ -234,13 +234,13 @@ print(f)
 lon = f.coordinate('longitude')
 lon.dump()
 lon.get_geometry()
-print(lon.bounds.data.array)
-print(lon.get_interior_ring().data.array)
+print(lon.bounds.array)
+print(lon.get_interior_ring().array)
 a = t.constructs.get('domainancillary0')
-print(a.data.array)
+print(a.array)
 bounds = a.bounds
 bounds
-print(bounds.data.array)
+print(bounds.array)
 crs = t.coordinate_reference('standard_name:atmosphere_hybrid_height_coordinate')
 crs
 crs.dump()
@@ -657,18 +657,18 @@ cfdm.write(g, 'new_parent.nc')
 cfdm.write(g, 'new_parent.nc', external='new_external.nc')
 h = cfdm.read('contiguous.nc')[0]
 print(h)
-print(h.data.array)
+print(h.array)
 h.data.get_compression_type()
 print(h.data.compressed_array)
 count_variable = h.data.get_count()
 count_variable
-print(count_variable.data.array)
+print(count_variable.array)
 station2 = h[1]
 station2
-print(station2.data.array)
+print(station2.array)
 h.data.get_compression_type()
 h.data[1, 2] = -9
-print(h.data.array)
+print(h.array)
 h.data.get_compression_type()
 # Start of code block
 
@@ -701,12 +701,12 @@ inplace=True)
 
 # End of code block
 T
-print(T.data.array)
+print(T.array)
 T.data.get_compression_type()
 print(T.data.compressed_array)
 count_variable = T.data.get_count()
 count_variable
-print(count_variable.data.array)
+print(count_variable.array)
 cfdm.write(T, 'T_contiguous.nc')
 # Start of code block
 
@@ -747,12 +747,12 @@ T.set_data(cfdm.Data(array), axes=[Y, X])
 # End of code block
 p = cfdm.read('gathered.nc')[0]
 print(p)
-print(p.data.array)
+print(p.array)
 p.data.get_compression_type()
 print(p.data.compressed_array)
 list_variable = p.data.get_list()
 list_variable
-print(list_variable.data.array)
+print(list_variable.array)
 p[0]
 p[1, :, 3:5]
 p.data.get_compression_type()
@@ -795,25 +795,25 @@ P.set_data(cfdm.Data(array), axes=[T, Y, X])
 
 # End of code block
 P
-print(P.data.array)
+print(P.array)
 P.data.get_compression_type()
 print(P.data.compressed_array)
 list_variable = P.data.get_list()
 list_variable
-print(list_variable.data.array)
+print(list_variable.array)
 cfdm.write(P, 'P_gathered.nc')
 f = cfdm.read('subsampled.nc')[0]
 print(f)
 lon = f.construct('longitude')
 lon
 lon.data.source()
-print(lon.data.array)
+print(lon.array)
 lon.data.source().source()
 print(lon.data.source().source().array)
-print(lon.data.array)
+print(lon.array)
 g = f[0, 6, :]
 print(g)
-print(g.construct('longitude').data.array)
+print(g.construct('longitude').array)
 lon = f.construct('longitude')
 d = lon.data.source()
 d.get_tie_point_indices()
diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst
index 71376f984..934f482ea 100644
--- a/docs/source/tutorial.rst
+++ b/docs/source/tutorial.rst
@@ -711,7 +711,7 @@ access.
 .. code-block:: python
    :caption: *Retrieve a numpy array of the data.*
       
-   >>> print(t.data.array)
+   >>> print(t.array)
    [[[262.8 270.5 279.8 269.5 260.9 265.0 263.5 278.9 269.2]
      [272.7 268.4 279.5 278.9 263.8 263.3 274.2 265.7 279.5]
      [269.7 279.1 273.4 274.2 279.6 270.2 280.0 272.5 263.7]
@@ -722,7 +722,7 @@ access.
      [267.9 273.5 279.8 260.3 261.2 275.3 271.2 260.8 268.9]
      [270.9 278.7 273.2 261.7 271.6 265.8 273.0 278.5 266.4]
      [276.4 264.2 276.3 266.1 276.1 268.1 277.0 273.4 269.7]]]
-
+     
 .. code-block:: python
    :caption: *Inspect the data type, number of dimensions, dimension
              sizes and number of elements of the data.*
@@ -977,7 +977,7 @@ in the dataset, may subsequently be applied manually with the
 
    >>> cfdm.write(q, 'masked_q.nc')
    >>> no_mask_q = cfdm.read('masked_q.nc', mask=False)[0]
-   >>> print(no_mask_q.data.array)
+   >>> print(no_mask_q.array)
    [9.96920997e+36, 9.96920997e+36, 9.96920997e+36, 9.96920997e+36,
     9.96920997e+36, 9.96920997e+36, 9.96920997e+36, 9.96920997e+36],
     [0.023 0.036 0.045 0.062 0.046 0.073 0.006 0.066]
@@ -986,7 +986,7 @@ in the dataset, may subsequently be applied manually with the
    [9.96920997e+36, 9.96920997e+36, 9.96920997e+36, 9.96920997e+36,
     9.96920997e+36, 9.96920997e+36, 9.96920997e+36, 9.96920997e+36]])
    >>> masked_q = no_mask_q.apply_masking()
-   >>> print(masked_q.data.array)
+   >>> print(masked_q.array)
    [[   --    --    --    --    --    --    --    --]
     [0.023 0.036 0.045 0.062 0.046 0.073 0.006 0.066]
     [0.11  0.131 0.124 0.146 0.087 0.103 0.057 0.011]
@@ -1067,7 +1067,7 @@ A single value may be assigned to any number of elements.
    >>> t.data[:, 0, 0] = -1
    >>> t.data[:, :, 1] = -2
    >>> t.data[..., 6:3:-1, 3:6] = -3
-   >>> print(t.data.array)
+   >>> print(t.array)
    [[[ -1.0  -2.0 279.8 269.5 260.9 265.0 263.5 278.9 269.2]
      [272.7  -2.0 279.5 278.9 263.8 263.3 274.2 265.7 279.5]
      [269.7  -2.0 273.4 274.2 279.6 270.2 280.0 272.5 263.7]
@@ -1090,7 +1090,7 @@ the shape defined by the indices, using the `numpy broadcasting rules
 	     
    >>> t.data[..., 6:3:-1, 3:6] = numpy.arange(9).reshape(3, 3)
    >>> t.data[0, [2, 9], [4, 8]] =  cfdm.Data([[-4, -5]])
-   >>> print(t.data.array)
+   >>> print(t.array)
    [[[ -1.0  -2.0 279.8 269.5 260.9 265.0 263.5 278.9 269.2]
      [272.7  -2.0 279.5 278.9 263.8 263.3 274.2 265.7 279.5]
      [269.7  -2.0 273.4 274.2  -4.0 270.2 280.0 272.5  -5.0]
@@ -1112,7 +1112,7 @@ assigning them to any other value.
 	     
    >>> t.data[0, :, -2] = cfdm.masked
    >>> t.data[0, 5, -2] = -6
-   >>> print(t.data.array)
+   >>> print(t.array)
    [[[ -1.0  -2.0 279.8 269.5 260.9 265.0 263.5    -- 269.2]
      [272.7  -2.0 279.5 278.9 263.8 263.3 274.2    -- 279.5]
      [269.7  -2.0 273.4 274.2  -4.0 270.2 280.0    --  -5.0]
@@ -1653,7 +1653,7 @@ construct <Data>` as the field construct for accessing their data:
    >>> lon.data[2]
    <Data(1): [112.5] degrees_east>
    >>> lon.data[2] = 133.33
-   >>> print(lon.data.array)
+   >>> print(lon.array)
    [22.5 67.5 133.33 157.5 202.5 247.5 292.5 337.5]
 
 The :term:`domain axis constructs` spanned by a particular metadata
@@ -1711,8 +1711,9 @@ Constructs representing elapsed time (identified by the presence of
 "reference time" units) have data array values that represent elapsed
 time since a reference date. These values may be converted into the
 date-time objects of the `cftime package
-<https://unidata.github.io/cftime/>`_ with the `~Data.datetime_array`
-method of the `Data` instance.
+<https://unidata.github.io/cftime/>`_ with the `~datetime_array`
+methods of any construct that contains data, as well as `Data`
+instances.
 
 .. code-block:: python
    :caption: *Inspect the the values of a "time" construct as elapsed
@@ -1725,9 +1726,9 @@ method of the `Data` instance.
    'days since 2018-12-01'
    >>> time.get_property('calendar', default='standard')
    'standard'
-   >>> print(time.data.array)
+   >>> print(time.array)
    [ 31.]
-   >>> print(time.data.datetime_array)
+   >>> print(time.datetime_array)
    [cftime.DatetimeGregorian(2019, 1, 1, 0, 0, 0, 0, 1, 1)]
 
 ----
@@ -1868,7 +1869,7 @@ construct <Data>` for accessing its data.
    <Bounds: grid_longitude(9, 2) >
    >>> bounds.data
    <Data(9, 2): [[-4.92, ..., -0.96]]>
-   >>> print(bounds.data.array)
+   >>> print(bounds.array)
    [[-4.92 -4.48]
     [-4.48 -4.04]
     [-4.04 -3.6 ]
@@ -1954,7 +1955,7 @@ missing data.
 .. code-block:: python
    :caption: *Inspect the geometry nodes.*
  
-   >>> print(lon.bounds.data.array)
+   >>> print(lon.bounds.array)
    [[20.0 10.0  0.0   --]
     [ 5.0 10.0 15.0 10.0]
     [20.0 10.0  0.0   --]]
@@ -1975,7 +1976,7 @@ polygon is to be included or excluded from the cell, with values of
 .. code-block:: python
    :caption: *Inspect the interior ring information.*
  
-   >>> print(lon.get_interior_ring().data.array)
+   >>> print(lon.get_interior_ring().array)
    [[0  1  0]
     [0 -- --]]
 
@@ -2011,12 +2012,12 @@ data array.
              construct.*
       
    >>> a = t.constructs.get('domainancillary0')
-   >>> print(a.data.array)
+   >>> print(a.array)
    [10.]
    >>> bounds = a.bounds
    >>> bounds
    <Bounds: ncvar%a_bounds(1, 2) >
-   >>> print(bounds.data.array)
+   >>> print(bounds.array)
    [[  5.  15.]]
 
 ----
@@ -4263,7 +4264,7 @@ file:
                    : longitude(ncdim%station(4)) = [-23.0, ..., 178.0] degrees_east
                    : height(ncdim%station(4)) = [0.5, ..., 345.0] m
                    : cf_role:timeseries_id(ncdim%station(4)) = [station1, ..., station4]
-   >>> print(h.data.array)
+   >>> print(h.array)
    [[0.12 0.05 0.18   --   --   --   --   --   --]
     [0.05 0.11 0.2  0.15 0.08 0.04 0.06   --   --]
     [0.15 0.19 0.15 0.17 0.07   --   --   --   --]
@@ -4281,7 +4282,7 @@ file:
    >>> count_variable = h.data.get_count()
    >>> count_variable
    <Count: long_name=number of observations for this station(4) >
-   >>> print(count_variable.data.array)
+   >>> print(count_variable.array)
    [3 7 5 9]
 
 The timeseries for the second station is easily selected by indexing
@@ -4293,7 +4294,7 @@ the "station" axis of the field construct:
    >>> station2 = h[1]
    >>> station2
    <Field: specific_humidity(ncdim%station(1), ncdim%timeseries(9))>
-   >>> print(station2.data.array)
+   >>> print(station2.array)
    [[0.05 0.11 0.2 0.15 0.08 0.04 0.06 -- --]]
 
 The underlying array of original data remains in compressed form until
@@ -4306,7 +4307,7 @@ data array elements are modified:
    >>> h.data.get_compression_type()
    'ragged contiguous'
    >>> h.data[1, 2] = -9
-   >>> print(h.data.array)
+   >>> print(h.array)
    [[0.12 0.05 0.18   --   --   --   --   --   --]
     [0.05 0.11 -9.0 0.15 0.08 0.04 0.06   --   --]
     [0.15 0.19 0.15 0.17 0.07   --   --   --   --]
@@ -4360,7 +4361,7 @@ The new field construct can now be inspected and written to a netCDF file:
    
    >>> T
    <Field: air_temperature(key%domainaxis1(2), key%domainaxis0(4)) K>
-   >>> print(T.data.array)
+   >>> print(T.array)
    [[280.0    --    --    --]
     [281.0 279.0 278.0 279.5]]
    >>> T.data.get_compression_type()
@@ -4370,7 +4371,7 @@ The new field construct can now be inspected and written to a netCDF file:
    >>> count_variable = T.data.get_count()
    >>> count_variable
    <Count: long_name=number of obs for this timeseries(2) >
-   >>> print(count_variable.data.array)
+   >>> print(count_variable.array)
    [1 4]
    >>> cfdm.write(T, 'T_contiguous.nc')
 
@@ -4516,7 +4517,7 @@ file:
    Dimension coords: time(2) = [2000-02-01 00:00:00, 2000-03-01 00:00:00]
                    : latitude(4) = [-90.0, ..., -75.0] degrees_north
                    : longitude(5) = [0.0, ..., 40.0] degrees_east
-   >>> print(p.data.array)
+   >>> print(p.array)
    [[[--       0.000122 0.0008   --       --      ]
      [0.000177 --       0.000175 0.00058  --      ]
      [--       --       --       --       --      ]
@@ -4539,7 +4540,7 @@ file:
    >>> list_variable = p.data.get_list()
    >>> list_variable
    <List: ncvar%landpoint(7) >
-   >>> print(list_variable.data.array)
+   >>> print(list_variable.array)
    [1 2 5 7 8 16 18]
 
 Subspaces based on the uncompressed axes of the field construct are
@@ -4625,7 +4626,7 @@ The new field construct can now be inspected and written a netCDF file:
    
    >>> P
    <Field: precipitation_flux(key%domainaxis0(2), key%domainaxis1(3), key%domainaxis2(2)) kg m-2 s-1>
-   >>> print(P.data.array)
+   >>> print(P.array)
    [[[ -- 2.0]
      [ --  --]
      [1.0 3.0]]
@@ -4641,7 +4642,7 @@ The new field construct can now be inspected and written a netCDF file:
    >>> list_variable = P.data.get_list()
    >>> list_variable 
    <List: (3) >
-   >>> print(list_variable.data.array)
+   >>> print(list_variable.array)
    [1 4 5]
    >>> cfdm.write(P, 'P_gathered.nc')
 
@@ -4761,7 +4762,7 @@ still in subsampled representation described in the file:
    <AuxiliaryCoordinate: longitude(18, 12) degrees_east>
    >>> lon.data.source()
    <SubsampledArray(18, 12): >
-   >>> print(lon.data.array)
+   >>> print(lon.array)
    [[15.0 45.0 75.0 105.0 135.0 165.0 195.0 225.0 255.0 285.0 315.0 345.0]
     [15.0 45.0 75.0 105.0 135.0 165.0 195.0 225.0 255.0 285.0 315.0 345.0]
     [15.0 45.0 75.0 105.0 135.0 165.0 195.0 225.0 255.0 285.0 315.0 345.0]
@@ -4788,7 +4789,7 @@ still in subsampled representation described in the file:
     [ 15. 135. 225. 255. 345.]
     [ 15. 135. 225. 255. 345.]]
 
-   >>> print(lon.data.array)
+   >>> print(lon.array)
 As with all other forms of compression, the field may be treated as if
 were not compressed:
 
@@ -4804,7 +4805,7 @@ were not compressed:
    Dimension coords: time(1) = [2000-01-01 00:00:00]
    Auxiliary coords: latitude(ncdim%lat(1), ncdim%lon(12)) = [[-25.0, ..., -25.0]] degrees_north
                    : longitude(ncdim%lat(1), ncdim%lon(12)) = [[15.0, ..., 345.0]] degrees_east
-   >>> print(g.construct('longitude').data.array)
+   >>> print(g.construct('longitude').array)
    [[15.0 45.0 75.0 105.0 135.0 165.0 195.0 225.0 255.0 285.0 315.0 345.0]]
 
 


Test patch:
diff --git a/cfdm/test/test_DimensionCoordinate.py b/cfdm/test/test_DimensionCoordinate.py
index 7a617a0f4..0513f9ada 100644
--- a/cfdm/test/test_DimensionCoordinate.py
+++ b/cfdm/test/test_DimensionCoordinate.py
@@ -3,7 +3,8 @@ import faulthandler
 import os
 import unittest
 
-import numpy
+import cftime
+import numpy as np
 
 faulthandler.enable()  # to debug seg faults and timeouts
 
@@ -19,7 +20,7 @@ class DimensionCoordinateTest(unittest.TestCase):
 
     dim = cfdm.DimensionCoordinate()
     dim.set_property("standard_name", "latitude")
-    a = numpy.array(
+    a = np.array(
         [
             -30,
             -23.5,
@@ -38,7 +39,7 @@ class DimensionCoordinateTest(unittest.TestCase):
     )
     dim.set_data(cfdm.Data(a, "degrees_north"))
     bounds = cfdm.Bounds()
-    b = numpy.empty(a.shape + (2,))
+    b = np.empty(a.shape + (2,))
     b[:, 0] = a - 0.1
     b[:, 1] = a + 0.1
     bounds.set_data(cfdm.Data(b))
@@ -107,6 +108,23 @@ class DimensionCoordinateTest(unittest.TestCase):
         self.assertTrue(x.del_climatology())
         self.assertIsNone(x.del_climatology(None))
 
+    def test_DimensiconCoordinate_array(self):
+        """Test the `DimensionCoordinate.array` method."""
+        f = cfdm.example_field(0)
+        t = f.construct("time")
+        self.assertEqual(t.array, 31)
+        self.assertEqual(t.array, t.data.array)
+
+    def test_DimensiconCoordinate_datetime_array(self):
+        """Test the `DimensionCoordinate.datetime_array` method."""
+        f = cfdm.example_field(0)
+        t = f.construct("time")
+        self.assertEqual(
+            t.datetime_array,
+            np.array([cftime.DatetimeGregorian(2019, 1, 1, 0, 0, 0, 0)]),
+        )
+        self.assertEqual(t.datetime_array, t.data.datetime_array)
+
 
 if __name__ == "__main__":
     print("Run date:", datetime.datetime.now())
diff --git a/cfdm/test/test_read_write.py b/cfdm/test/test_read_write.py
index 654768e64..e2b5791b6 100644
--- a/cfdm/test/test_read_write.py
+++ b/cfdm/test/test_read_write.py
@@ -851,7 +851,7 @@ class read_writeTest(unittest.TestCase):
 
         with self.assertRaises(OSError):
             cfdm.read(tmpfile)
-            
+
     def test_read_subsampled_coordinates(self):
         """Test the reading subsampled coordinates."""
         for i in cfdm.read("subsampled_2.nc"):