<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def get_gene_sets(self, min_genes=None, max_genes=None):
 """Return the set of annotated genes for each GO term. Parameters min_genes: int, optional Exclude GO terms with fewer than this number of genes. max_genes: int, optional Exclude GO terms with more than this number of genes. Returns ------- GeneSetCollection A gene set "database" with one gene set for each GO term. """


        if not self.terms:
            raise ValueError('You need to first parse both an OBO file and '
                             'a gene association file!')

        if not self.annotations:
            raise ValueError('You need to first parse a gene association '
                             'file!')

        all_term_ids = sorted(self.terms.keys())

        # go over all GO terms and get associated genes
        logger.info('Obtaining GO term associations...')
        # n = len(all_term_ids)
        # term_gene_counts = []
        # term_ids = []

        term_genes = OrderedDict()
        geneset_terms = {}
        gene_sets = []
        for j, id_ in enumerate(all_term_ids):
            tg = self.get_goterm_genes(id_)
            assert isinstance(tg, frozenset)
            c = len(tg)

            if c == 0:
                continue

            if (min_genes is not None and c < min_genes) or \
                    (max_genes is not None and c > max_genes):
                # term doesn't meet min/max number of genes criteria
                continue

            # for finding redundant terms (use set of genes as key)
            try:
                geneset_terms[tg].append(id_)
            except KeyError:
                geneset_terms[tg] = [id_]

            term_genes[id_] = tg

        selected = len(term_genes)
        affected = 0
        excl = 0
        for id_, tg in term_genes.items():

            # check if there are redundant terms
            term = self.terms[id_]
            if len(geneset_terms[tg]) > 1:
                gt = geneset_terms[tg]
                affected += 1
                # check if this term is an ancestor of any of them
                # if so, exclude it
                excluded = False
                for other_id in gt:
                    if (other_id != id_) and (other_id in term.descendants):
                        excluded = True
                        break
                if excluded:
                    excl += 1
                    continue

            # if the term is not redundant with any other term,
            # or if it isn't the ancestor of any redundant term,
            # add its gene set to the list
            name = term.name
            source = 'GO'
            coll = term.domain_short
            desc = term.definition
            gs = GeneSet(id_, name, tg, source=source,
                         collection=coll, description=desc)
            gene_sets.append(gs)

        D = GeneSetCollection(gene_sets)
        logger.info('# terms selected intially: %d', selected)
        logger.info('# terms with redundant gene sets: %d', affected)
        logger.info('# terms excluded due to redundancy: %d', excl)
        logger.info('# terms retained: %d', D.n)

        return D