<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def find_items(self, q, shape=ID_ONLY, depth=SHALLOW, additional_fields=None, order_fields=None, calendar_view=None, page_size=None, max_items=None, offset=0):
 """ Private method to call the FindItem service :param q: a Q instance containing any restrictions :param shape: controls whether to return (id, chanegkey) tuples or Item objects. If additional_fields is non-null, we always return Item objects. :param depth: controls the whether to return soft-deleted items or not. :param additional_fields: the extra properties we want on the return objects. Default is no properties. Be aware that complex fields can only be fetched with fetch() (i.e. the GetItem service). :param order_fields: the SortOrder fields, if any :param calendar_view: a CalendarView instance, if any :param page_size: the requested number of items per page :param max_items: the max number of items to return :param offset: the offset relative to the first item in the item collection :return: a generator for the returned item IDs or items """

        if shape not in SHAPE_CHOICES:
            raise ValueError("'shape' %s must be one of %s" % (shape, SHAPE_CHOICES))
        if depth not in ITEM_TRAVERSAL_CHOICES:
            raise ValueError("'depth' %s must be one of %s" % (depth, ITEM_TRAVERSAL_CHOICES))
        if not self.folders:
            log.debug('Folder list is empty')
            return
        if additional_fields:
            for f in additional_fields:
                self.validate_item_field(field=f)
            for f in additional_fields:
                if f.field.is_complex:
                    raise ValueError("find_items() does not support field '%s'. Use fetch() instead" % f.field.name)
        if calendar_view is not None and not isinstance(calendar_view, CalendarView):
            raise ValueError("'calendar_view' %s must be a CalendarView instance" % calendar_view)

        # Build up any restrictions
        if q.is_empty():
            restriction = None
            query_string = None
        elif q.query_string:
            restriction = None
            query_string = Restriction(q, folders=self.folders, applies_to=Restriction.ITEMS)
        else:
            restriction = Restriction(q, folders=self.folders, applies_to=Restriction.ITEMS)
            query_string = None
        log.debug(
            'Finding %s items in folders %s (shape: %s, depth: %s, additional_fields: %s, restriction: %s)',
            self.folders,
            self.account,
            shape,
            depth,
            additional_fields,
            restriction.q if restriction else None,
        )
        items = FindItem(account=self.account, folders=self.folders, chunk_size=page_size).call(
            additional_fields=additional_fields,
            restriction=restriction,
            order_fields=order_fields,
            shape=shape,
            query_string=query_string,
            depth=depth,
            calendar_view=calendar_view,
            max_items=calendar_view.max_items if calendar_view else max_items,
            offset=offset,
        )
        if shape == ID_ONLY and additional_fields is None:
            for i in items:
                yield i if isinstance(i, Exception) else Item.id_from_xml(i)
        else:
            for i in items:
                if isinstance(i, Exception):
                    yield i
                else:
                    yield Folder.item_model_from_tag(i.tag).from_xml(elem=i, account=self.account)