.. currentmodule:: cf .. default-role:: obj .. _fs_field_list: Introduction to the `cf.FieldList` object ========================================= A `cf.FieldList` object is an ordered sequence of `cf.Field` objects. It supports all of the :ref:`python list-like operations ` with the exception of the arithmetic and comparison operators, for which it has :ref:`its own definitions `. For example: >>> fl [, ] >>> fl[0] >>> fl[::-1] [, ] >>> len(fl) 2 >>> f = fl.pop() >>> f >>> 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 `~cf.FieldList.item`) or a reserved attribute or CF property (such as `~cf.FieldList.Units` or `~cf.FieldList.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 [, ] >>> fl.subspace[0, ...] [, ] 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') [, ] 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 `~cf.List.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 `~cf.List.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 `~cf.FieldList.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 .. _fs-fl-a-and-c: Arithmetic and comparison ^^^^^^^^^^^^^^^^^^^^^^^^^ Any arithmetic and comparison operation is applied independently to each field element, so all of the :ref:`operators defined for a field ` are allowed. In particular, the usual :ref:`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 >>> f is f[0] is f[slice(-1, None, -1)] is f[::-1] True >>> for g in f: ... print repr(g) ... 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 `~cf.List.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 `~cf.FieldList.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