| t | def Record(slots_str, **readonly_fields): | t | def Record(slots_str, **readonly_fields): | 
            |  |  |  |  | 
            |  | def decorator(cls): |  | def decorator(cls): | 
            |  | if not hasattr(cls, '__slots__'): |  | if not hasattr(cls, '__slots__'): | 
            |  | raise TypeError('Class must use __slots__ as its object model') |  | raise TypeError('Class must use __slots__ as its object model') | 
            |  | new_slots = sorted(set(cls.__slots__) | set(slots_str.split())) |  | new_slots = sorted(set(cls.__slots__) | set(slots_str.split())) | 
            |  | setattr(cls, '__slots__', new_slots) |  | setattr(cls, '__slots__', new_slots) | 
            |  | for field in dir(cls): |  | for field in dir(cls): | 
            |  | if field.startswith('_') is False: |  | if field.startswith('_') is False: | 
            |  | setattr(cls, field, getattr(cls, field)) |  | setattr(cls, field, getattr(cls, field)) | 
            |  | for field, value in readonly_fields.items(): |  | for field, value in readonly_fields.items(): | 
            |  | if field.startswith('_') is False: |  | if field.startswith('_') is False: | 
            |  | setattr(cls, field, value) |  | setattr(cls, field, value) | 
            |  |  |  |  | 
            |  | def set_obj(self, attr, value): |  | def set_obj(self, attr, value): | 
            |  | if attr in cls.__slots__: |  | if attr in cls.__slots__: | 
            |  | setattr(cls, attr, value) |  | setattr(cls, attr, value) | 
            |  | cls.__setattr__ = set_obj |  | cls.__setattr__ = set_obj | 
            |  |  |  |  | 
            |  | def iter_obj(self): |  | def iter_obj(self): | 
            |  | for attr in sorted(set(dir(cls) + cls.__slots__)): |  | for attr in sorted(set(dir(cls) + cls.__slots__)): | 
            |  | if not attr.startswith('_'): |  | if not attr.startswith('_'): | 
            |  | yield attr |  | yield attr | 
            |  | cls.__iter__ = iter_obj |  | cls.__iter__ = iter_obj | 
            |  |  |  |  | 
            |  | def str_obj(self): |  | def str_obj(self): | 
            |  | parts = [] |  | parts = [] | 
            |  | for attr in iter_obj(self): |  | for attr in iter_obj(self): | 
            |  | value = getattr(self, attr, None) |  | value = getattr(self, attr, None) | 
            |  | if attr in cls.__slots__ and value is not None: |  | if attr in cls.__slots__ and value is not None: | 
            |  | parts.append(f'{attr}={value}') |  | parts.append(f'{attr}={value}') | 
            |  | elif value is not None: |  | elif value is not None: | 
            |  | parts.append(f'{attr}:{value}') |  | parts.append(f'{attr}:{value}') | 
            |  | else: |  | else: | 
            |  | parts.append(attr) |  | parts.append(attr) | 
            |  | return '|'.join(parts) |  | return '|'.join(parts) | 
            |  | cls.__str__ = str_obj |  | cls.__str__ = str_obj | 
            |  | return cls |  | return cls | 
            |  | return decorator |  | return decorator |