def _function(self):
        '''
        Runs either a loop or a parameter sweep over the subscripts in the order defined by the parameter_list 'script_order'
        '''
        def get_sweep_parameters():
            """
            Returns: the paramter values over which to sweep
            """
            #in both cases, param values have tolist to make sure that they are python types (ex float) rather than numpy
            #types (ex np.float64), the latter of which can cause typing issues
            sweep_range = self.settings['sweep_range']
            if self.settings['stepping_mode'] == 'N':
                param_values = np.linspace(sweep_range['min_value'], sweep_range['max_value'],
                                           int(sweep_range['N/value_step']), endpoint=True).tolist()
            elif self.settings['stepping_mode'] == 'value_step':
                param_values = np.linspace(sweep_range['min_value'], sweep_range['max_value'],
                                           (sweep_range['max_value'] - sweep_range['min_value']) / sweep_range[
                                               'N/value_step'] + 1, endpoint=True).tolist()
            return param_values

        script_names = list(self.settings['script_order'].keys())
        script_indices = [self.settings['script_order'][name] for name in script_names]
        _, sorted_script_names = list(zip(*sorted(zip(script_indices, script_names))))

        if self.iterator_type == 'sweep':


            def get_script_and_settings_from_str(sweep_param):
                """
                Args:
                    sweep_param: astring with the path to the sweep parameter
                        e.g. script.param.subparam or script->subscript.parameter

                Returns:
                    script_list: a list with the scripts, e.g. [script] or [script, subscript]
                    parameter_list: a list with the paraemeters, e.g. [param, subparam] or [parameter] for the cases above
                """
                split_trace = sweep_param.split('.')
                script_list = split_trace[0].split('->')
                parameter_list = split_trace[1:]

                return script_list, parameter_list

            param_values = get_sweep_parameters()

            print('JG parametes before', param_values)
            if self.settings['sweep_range']['randomize'] == True:
                random.shuffle(param_values)
            print('JG parametes after', param_values)

            for i, value in enumerate(param_values):
                self.iterator_progress = float(i) / len(param_values)

                script_list, parameter_list = get_script_and_settings_from_str(self.settings['sweep_param'])
                script = self
                while len(script_list)>0:
                    script = script.scripts[script_list[0]]
                    script_list = script_list[1:]

                curr_type = type(reduce(lambda x,y: x[y], parameter_list, script.settings)) #traverse nested dict to get type of variable

                update_dict = reduce(lambda y, x: {x: y}, reversed(parameter_list), curr_type(value)) #creates nested dictionary from list

                script.settings.update(update_dict)
                parameter_name = parameter_list[-1]
                if np.abs(value) < 1000:
                    self.log('setting parameter {:s} to {:.3g}'.format(self.settings['sweep_param'], value))
                else:
                    self.log('setting parameter {:s} to {:0.2e}'.format(self.settings['sweep_param'], value))

                for script_name in sorted_script_names:
                    if self._abort:
                        break
                    j = i if self.settings['run_all_first'] else (i+1)

                    curr_script_exec_freq = self.settings['script_execution_freq'][script_name]
                    if curr_script_exec_freq != 0 and (j % curr_script_exec_freq == 0):
                        # i+1 so first execution is mth loop, not first
                        self.log('starting {:s}'.format(script_name))
                        tag = self.scripts[script_name].settings['tag']
                        self.scripts[script_name].settings['tag'] = '{:s}_{:s}_{:0.3e}'.format(tag, parameter_name, value)
                        self.scripts[script_name].run()
                        self.scripts[script_name].settings['tag'] = tag

        elif self.iterator_type == 'loop':

            num_loops = self.settings['num_loops']
            if num_loops == 0:
                self.log('Loop set to run 0 times')
                return

            self.data = {}
            for i in range(num_loops):
                self.iterator_progress = float(i) / num_loops

                for script_name in sorted_script_names:
                    if self._abort:
                        break
                    j = i if self.settings['run_all_first'] else (i+1)

                    curr_script_execution_freq = self.settings['script_execution_freq'][script_name]

                    if curr_script_execution_freq != 0 and (j % curr_script_execution_freq == 0):
                        # i+1 so first execution is mth loop, not first
                        self.log('starting {:s} \t iteration {:d} of {:d}'.format(script_name, i + 1, num_loops))
                        tag = self.scripts[script_name].settings['tag']
                        tmp = tag + '_{' + ':0{:d}'.format(len(str(num_loops))) + '}'
                        self.scripts[script_name].settings['tag'] = tmp.format(i)
                        self.scripts[script_name].run()
                        self.scripts[script_name].settings['tag'] = tag

                # from the last script we take the average of the data as the data of the iterator script
                if isinstance(self.scripts[script_name].data, dict):
                    data = self.scripts[script_name].data
                elif isinstance(self.scripts[script_name].data, deque):
                    data = self.scripts[script_name].data[-1]
                if i == 0:
                    self.data.update(data)
                else:
                    if self._abort:
                        break

                    for key in list(data.keys()):

                        # can't add None values
                        if not data[key] is None:
                            # if subscript data have differnet length, e.g. fitparameters can be differet, depending on if there is one or two peaks
                            if len(self.data[key]) != len(data[key]):
                                print(('warning subscript data {:s} have different lengths'.format(key)))
                                continue

                            if isinstance(self.data[key], list):
                                self.data[key] += np.array(data[key])
                            elif isinstance(self.data[key], dict):
                                self.data[key] = {x: self.data[key].get(x, 0) + data[key].get(x, 0) for x in list(self.data[key].keys())}
                            else:
                                self.data[key] += data[key]

            if not self._abort and num_loops > 0:
                # normalize data because we just kept adding the values
                for key in list(data.keys()):
                    if isinstance(self.data[key], list):
                        self.data[key] = np.array(self.data[key]) / num_loops
                    elif isinstance(self.data[key], dict):
                        self.data[key] = {k:v/num_loops for k, v in self.data[key].items()}
                    elif self.data[key] is None:
                        self.log('None type in data! check code')
                        pass
                    elif isinstance(self.data[key], int):
                        self.data[key] = float(self.data[key]) / num_loops # if int we can not devide. Thus we convert explicitely to float
                    else:
                        self.data[key] = self.data[key] / num_loops

        else:
            raise TypeError('wrong iterator type')