def measure(
    *target,
    key: str | cirq.MeasurementKey | None = None,
    invert_mask: tuple[bool, ...] = (),
    confusion_map: dict[tuple[int, ...], np.ndarray] | None = None,
) -> raw_types.Operation:
    """Returns a single MeasurementGate applied to all the given qubits.

    The qubits are measured in the computational basis. This can also be
    used with the alias `cirq.M`.

    Args:
        *target: The qubits that the measurement gate should measure.
            These can be specified as separate function arguments or
            with a single argument for an iterable of qubits.
        key: Optional `str` or `cirq.MeasurementKey` that gate should use.
            If none provided, it defaults to a comma-separated list of
            `str(qubit)` for each of the target qubits.
        invert_mask: A list of Truthy or Falsey values indicating whether
            the corresponding qubits should be flipped. None indicates no
            inverting should be done.
        confusion_map: A map of qubit index sets (using indices in
            `target`) to the 2D confusion matrix for those qubits. Indices
            not included use the identity. Applied before invert_mask if both
            are provided.

    Returns:
        An operation targeting the given qubits with a measurement.

    Raises:
        ValueError: If the qubits are not instances of Qid.
    """
    one_iterable_arg: bool = (
        len(target) == 1
        and isinstance(target[0], Iterable)
        and not isinstance(target[0], (bytes, str, np.ndarray))
    )
    targets = tuple(target[0]) if one_iterable_arg else target
    for qubit in targets:
        if isinstance(qubit, np.ndarray):
            raise ValueError(
                'measure() was called a numpy ndarray. Perhaps you meant '
                'to call measure_state_vector on numpy array?'
            )
        elif not isinstance(qubit, raw_types.Qid):
            raise ValueError('measure() was called with type different than Qid.')

    if key is None:
        key = _default_measurement_key(targets)
    qid_shape = protocols.qid_shape(targets)
    return MeasurementGate(len(targets), key, invert_mask, qid_shape, confusion_map).on(*targets)
Returns a single MeasurementGate applied to all the given qubits.

    The qubits are measured in the computational basis. This can also be
    used with the alias `cirq.M`.

    Args:
        *target: The qubits that the measurement gate should measure.
            These can be specified as separate function arguments or
            with a single argument for an iterable of qubits.
        key: Optional `str` or `cirq.MeasurementKey` that gate should use.
            If none provided, it defaults to a comma-separated list of
            `str(qubit)` for each of the target qubits.
        invert_mask: A list of Truthy or Falsey values indicating whether
            the corresponding qubits should be flipped. None indicates no
            inverting should be done.
        confusion_map: A map of qubit index sets (using indices in
            `target`) to the 2D confusion matrix for those qubits. Indices
            not included use the identity. Applied before invert_mask if both
            are provided.

    Returns:
        An operation targeting the given qubits with a measurement.

    Raises:
        ValueError: If the qubits are not instances of Qid.
    
cirq-core/cirq/ops/measure_util.py