<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def normalize_per_cell(data, counts_per_cell_after=None, counts_per_cell=None, key_n_counts=None, max_proportion_per_cell=None, use_initial_size=True, layers=['spliced', 'unspliced'], enforce=False, copy=False):
 """Normalize each cell by total counts over all genes. Parameters data : :class:`~anndata.AnnData`, `np.ndarray`, `sp.sparse` The (annotated) data matrix of shape `n_obs` × `n_vars`. Rows correspond to cells and columns to genes. counts_per_cell_after : `float` or `None`, optional (default: `None`) If `None`, after normalization, each cell has a total count equal to the median of the *counts_per_cell* before normalization. counts_per_cell : `np.array`, optional (default: `None`) Precomputed counts per cell. key_n_counts : `str`, optional (default: `'n_counts'`) Name of the field in `adata.obs` where the total counts per cell are stored. max_proportion_per_cell : `int` (default: `None`) Exclude genes counts that account for more than a specific proportion of cell size, e.g. 0.05. use_initial_size : `bool` (default: `True`) Whether to use initial cell sizes oder actual cell sizes. layers : `str` or `list` (default: `{'spliced', 'unspliced'}`) Keys for layers to be also considered for normalization. copy : `bool`, optional (default: `False`) If an :class:`~anndata.AnnData` is passed, determines whether a copy is returned. Returns ------- Returns or updates `adata` with normalized version of the original `adata.X`, depending on `copy`. """

    adata = data.copy() if copy else data
    layers = adata.layers.keys() if layers is 'all' else [layers] if isinstance(layers, str) \
        else [layer for layer in layers if layer in adata.layers.keys()]
    layers = ['X'] + layers
    modified_layers = []

    for layer in layers:
        X = adata.X if layer is 'X' else adata.layers[layer]
        if not_yet_normalized(X) or enforce:
            counts = counts_per_cell if counts_per_cell is not None \
                else get_initial_size(adata, layer) if use_initial_size else get_size(adata, layer)
            if max_proportion_per_cell is not None and (0 < max_proportion_per_cell < 1):
                counts = counts_per_cell_quantile(X, max_proportion_per_cell, counts)
            # equivalent to scanpy.pp.normalize_per_cell(X, counts_per_cell_after, counts)
            counts_after = np.median(counts) if counts_per_cell_after is None else counts_per_cell_after
            counts /= counts_after + (counts_after == 0)
            counts += counts == 0  # to avoid division by zero
            if issparse(X):
                sparsefuncs.inplace_row_scale(X, 1 / counts)
            else:
                X /= np.array(counts[:, None])
            modified_layers.append(layer)

    adata.obs['n_counts' if key_n_counts is None else key_n_counts] = get_size(adata)
    if len(modified_layers) > 0:
        logg.info('Normalized count data:', ', '.join(modified_layers) + '.')

    return adata if copy else None