1313import tempfile
1414from abc import abstractmethod
1515from collections import OrderedDict , namedtuple
16+ from collections .abc import Mapping
1617from functools import reduce
1718from itertools import product
1819from typing import TYPE_CHECKING , Any , NamedTuple , Protocol , runtime_checkable
@@ -143,6 +144,41 @@ def __getitem__(self, key: Any) -> Any:
143144 ...
144145
145146
147+ class FieldsAccessor (Mapping ):
148+ """Read-only mapping of structured field views."""
149+
150+ def __init__ (self , field_views : dict [str , Any ]):
151+ self ._field_views = field_views
152+
153+ def __getitem__ (self , key : str ) -> Any :
154+ return self ._field_views [key ]
155+
156+ def __iter__ (self ) -> Iterator [str ]:
157+ return iter (self ._field_views )
158+
159+ def __len__ (self ) -> int :
160+ return len (self ._field_views )
161+
162+ def __setitem__ (self , key : str , value : object ) -> None :
163+ raise TypeError (f'assign through the field view, e.g. array.fields["{ key } "][:] = values' )
164+
165+ def copy (self ) -> dict [str , Any ]:
166+ return dict (self ._field_views )
167+
168+ def __or__ (self , other : object ) -> dict [str , Any ]:
169+ if not isinstance (other , Mapping ):
170+ return NotImplemented
171+ return self .copy () | dict (other )
172+
173+ def __ror__ (self , other : object ) -> dict [str , Any ]:
174+ if not isinstance (other , Mapping ):
175+ return NotImplemented
176+ return dict (other ) | self .copy ()
177+
178+ def __repr__ (self ) -> str :
179+ return repr (self ._field_views )
180+
181+
146182def is_documented_by (original ):
147183 def wrapper (target ):
148184 target .__doc__ = original .__doc__
@@ -3695,10 +3731,11 @@ def __init__(self, **kwargs):
36953731 base = kwargs .pop ("_base" , None )
36963732 super ().__init__ (kwargs ["_array" ], base = base )
36973733 # Accessor to fields
3698- self . _fields = {}
3734+ field_views = {}
36993735 if self .dtype .fields :
37003736 for field in self .dtype .fields :
3701- self ._fields [field ] = NDField (self , field )
3737+ field_views [field ] = NDField (self , field )
3738+ self ._fields = FieldsAccessor (field_views )
37023739
37033740 @property
37043741 def cparams (self ) -> blosc2 .CParams :
@@ -3747,14 +3784,14 @@ def vlmeta(self) -> dict:
37473784 return self .schunk .vlmeta
37483785
37493786 @property
3750- def fields (self ) -> dict :
3787+ def fields (self ) -> Mapping [ str , NDField ] :
37513788 """
3752- Dictionary with the fields of the structured array.
3789+ Read-only mapping with the fields of the structured array.
37533790
37543791 Returns
37553792 -------
3756- fields: dict
3757- A dictionary with the fields of the structured array.
3793+ fields: Mapping
3794+ A read-only mapping with the fields of the structured array.
37583795
37593796 See Also
37603797 --------
@@ -3770,6 +3807,8 @@ def fields(self) -> dict:
37703807 >>> sa = blosc2.zeros(shape, dtype=dtype)
37713808 >>> # Check that fields are equal
37723809 >>> assert sa.fields['a'] == sa.fields['b']
3810+ >>> # Assign through the field view
3811+ >>> sa.fields['a'][:] = 1
37733812 """
37743813 return self ._fields
37753814
0 commit comments