<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def create_variable( self, name, values=None, *, shape=None, units=None, dtype=None, **kwargs ) -> Variable: """Add new child variable. Parameters name : string Unique identifier. values : array-like (optional) Array to populate variable with. If None, an variable will be filled with NaN. Default is None. shape : tuple of int Shape to use. must broadcast with the full shape. Only used if `values` is None. Default is the full shape of self. units : string (optional) Variable units. Default is None. dtype : numpy.dtype (optional) dtype to use for dataset, default is np.float64. Only used if `values` is None. kwargs Additional kwargs to variable instantiation. Returns ------- WrightTools Variable New child variable. """

        if name in self.variable_names:
            warnings.warn(name, wt_exceptions.ObjectExistsWarning)
            return self[name]
        elif name in self.channel_names:
            raise wt_exceptions.NameNotUniqueError(name)
        if values is None:
            if shape is None:
                shape = self.shape
            if dtype is None:
                dtype = np.dtype(np.float64)
            if dtype.kind in "fcmM":
                fillvalue = np.nan
            else:
                fillvalue = 0
        else:
            shape = values.shape
            dtype = values.dtype
            fillvalue = None
        # create dataset
        id = self.require_dataset(
            name=name, data=values, shape=shape, dtype=dtype, fillvalue=fillvalue
        ).id
        variable = Variable(self, id, units=units, **kwargs)
        # finish
        self._variables = None
        self.attrs["variable_names"] = np.append(self.attrs["variable_names"], name.encode())
        return variable