Introduction to the cf.FieldList object

A cf.FieldList object is an ordered sequence of cf.Field objects.

It supports all of the python list-like operations with the exception of the arithmetic and comparison operators, for which it has its own definitions. For example:

>>> fl
[<CF Field: x_wind(grid_latitude(110), grid_longitude(106)) m s-1>,
 <CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>]
>>> fl[0]
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> fl[::-1]
[<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>,
 <CF Field: x_wind(grid_latitude(110), grid_longitude(106)) m s-1>]
>>> len(fl)
2
>>> f = fl.pop()
>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> len(fl)
1
>>> fl.append(f)
>>> len(fl)
2
>>> f in fl
True

Methods, attributes and CF properties

A field list object also has all of the callable methods, reserved attributes and reserved CF properties that a field object has. When used with a field list, a callable method (such as item) or a reserved attribute or CF property (such as Units or standard_name) is applied independently to each field and, unless a method (or assignment to a reserved attribute or CF property) carries out an in-place change to each field, a sequence of the results is returned.

The type of sequence that may be returned will either be a new cf.FieldList object or else a new cf.List object. For example, cf.FieldList.subspace will return a new field list of subspaced fields:

>>> fl
[<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>,
 <CF Field: air_pressure(time(12), latitude(73), longitude(96)) hPa>]
>>> fl.subspace[0, ...]
[<CF Field: air_temperature(time(1), latitude(73), longitude(96)) K>,
 <CF Field: air_pressure(time(1), latitude(73), longitude(96)) hPa>]

whereas cf.FieldList.ndim, cf.FieldList.standard_name and cf.FieldList.dim return a cf.List of integer, string and dimension coordinate objects respectively:

>>> fl.ndim
[3, 3]
>>> fl.standard_name
['air_temperature', 'air_pressure']
>>> fl.dim('time')
[<CF DimensionCoordinate: time(12) days since 1860-1-1>,
 <CF DimensionCoordinate: time(12) days since 1860-1-1>]

A cf.List object is very much like a field list, except that it may contain arbitrary element types (not just field objects) and has very few of the field list’s methodsasasdasds XXXXXXXXXXXXx and , in that it has all of the built-in list methods, but it also has an extra method, called method, which allows any callable method (with arguments) to be applied independently to each element of the list, returning the result in a new cf.List object:

A cf.List object is very much like a built-in list, in that it has all of the built-in list methods, but it also has an extra method, called method, which allows any callable method (with arguments) to be applied independently to each element of the list, returning the result in a new cf.List object:

>>> fl.standard_name[::-1]
['air_pressure', 'air_temperature']
>>> fl.standard_name.method('upper')
['AIR_TEMPERATURE', 'AIR_PRESSURE']
>>> fl.item('time').method('getprop', 'standard_name')
['time', 'time']
>>> fl.item('time').method('delrop')
[None, None]
>>> fl.item('time').method('setprop', 'standard_name', 'foo')
[None, None]
>>> fl.item('foo').method('getprop', 'standard_name')
['foo', 'foo']

The cf.FieldList object also has an equivalent method called method which behaves in an analogous way, thus reducing the need to know which type of sequence has been returned from a field list method:

>>> fl.getprop('standard_name') == fl.method('getprop', 'standard_name')
True

Assignment to reserved attributes and CF properties assigns the value to each field in turn. Similarly, deletion is carried out on each field:

>>> fl.standard_name
['air_pressure', 'air_temperature']
>>> fl.standard_name = 'foo'
['foo', 'foo']
>>> del fl.standard_name
>>> fl.getprop('standard_name', 'MISSING')
['MISSING', 'MISSING']

Note that the new value is not copied prior to each field assignment, which may be an issue for values which are mutable objects.

Changes tailored to each field in the list are easily carried out in a loop:

>>> for f in fl:
...     f.long_name = 'An even longer ' + f.long_name

Arithmetic and comparison

Any arithmetic and comparison operation is applied independently to each field element, so all of the operators defined for a field are allowed.

In particular, the usual python list-like arithmetic and comparison operator behaviours do not apply. For example, the + operator will concatenate two built-in lists, but adding 2 to a field list will add 2 to the data array of each of its fields.

For example these commands:

>>> fl + 2
>>> 2 + fl
>>> fl == 0
>>> fl += 2

are equivalent to:

>>> cf.FieldList(f + 2 for f in fl)
>>> cf.FieldList(2 + f for f in fl)
>>> cf.FieldList(f == 0 for f in fl)
>>> for f in fl:
...     f += 2

Field versus field list

In some contexts, whether an object is a field or a field list is not known. So to avoid ungainly type testing, most aspects of the cf.FieldList interface are shared by a cf.Field object.

A field may be used in the same iterative contexts as a field list:

>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> f is f[0] is f[slice(-1, None, -1)] is f[::-1]
True
>>> for g in f:
...     print repr(g)
...
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>

When it is not known whether or not you have a field or a field list, iterating over the output of a callable method could be complicated because the output of the field’s method will be a scalar whilst the output of the field list’s method will be a sequence of scalars. The problem is illustrated in this example:

>>> f = fl[0]
>>> for x in f.standard_name:
...     print x+'.',
...
a.i.r._.p.r.e.s.s.u.r.e.
>>> for x in fl.standard_name:
...     print x+'.',
...
air_pressure.air_temperature.

To overcome this difficulty, both the field and field list have a method call iter which has no effect on a field list, but which changes the output of a field’s callable method (with arguments) into a single element sequence:

>>> f = fl[0]
>>> for x in f.iter('getprop', 'standard_name'):
...     print x+'.',
...
air_pressure.
>>> for x in fl.iter('getprop', 'standard_name'):
...     print x+'.',
...
air_pressure.air_temperature.

However, it may be preferable to create a new field list to achiev the same result:

>>> for x in cf.FieldList(f).standard_name:
...     print x+'.',
...
air_pressure.
>>> for x in cf.FieldList(fl).standard_name:
...     print x+'.',
...
air_pressure.air_temperature.

The cf.List object

A cf.List object is very much like a built-in list, in that it has all of the built-in list methods, but it also has an extra method, called method, which allows any callable method (with arguments) to be applied independently to each element of the list, returning the result in a new cf.List object:

>>> fl.standard_name[::-1]
['air_pressure', 'air_temperature']
>>> fl.standard_name.method('upper')
['AIR_TEMPERATURE', 'AIR_PRESSURE']
>>> fl.item('time').method('getprop', 'standard_name')
['time', 'time']
>>> fl.item('time').method('delprop')
[None, None]
>>> fl.item('time').method('setprop', 'standard_name', 'foo')
[None, None]
>>> fl.item('time').method('getprop', 'standard_name')
['foo', 'foo']

The cf.FieldList object also has an equivalent method called method which behaves in an analogous way, thus reducing the need to know which type of sequence has been returned from a field list method:

>>> fl.getprop('standard_name') == fl.method('getprop', 'standard_name')
True

Assignment to reserved attributes and CF properties assigns the value to each field in turn. Similarly, deletion is carried out on each field:

>>> fl.standard_name
['air_pressure', 'air_temperature']
>>> fl.standard_name = 'foo'
['foo', 'foo']
>>> del fl.standard_name
>>> fl.getprop('standard_name', 'MISSING')
['MISSING', 'MISSING']

Note that the new value is not copied prior to each field assignment, which may be an issue for values which are mutable objects.

Changes tailored to each field in the list are easily carried out in a loop:

>>> for f in fl:
...     f.long_name = 'An even longer ' + f.long_name