<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_bonds(self, self_bonding_allowed=False, offset=3, modified_properties=None, use_lookup=False, set_lookup=True, atomic_radius_data=None ):
 """Return a dictionary representing the bonds. .. warning:: This function is **not sideeffect free**, since it assigns the output to a variable ``self._metadata['bond_dict']`` if ``set_lookup`` is ``True`` (which is the default). This is necessary for performance reasons. ``.get_bonds()`` will use or not use a lookup depending on ``use_lookup``. Greatly increases performance if True, but could introduce bugs in certain situations. Just imagine a situation where the :class:`~Cartesian` is changed manually. If you apply lateron a method e.g. :meth:`~get_zmat()` that makes use of :meth:`~get_bonds()` the dictionary of the bonds may not represent the actual situation anymore. You have two possibilities to cope with this problem. Either you just re-execute ``get_bonds`` on your specific instance, or you change the ``internally_use_lookup`` option in the settings. Please note that the internal use of the lookup variable greatly improves performance. Args: modified_properties (dic):
 If you want to change the van der Vaals radius of one or more specific atoms, pass a dictionary that looks like:: modified_properties = {index1: 1.5} For global changes use the constants module. offset (float):
 use_lookup (bool):
 set_lookup (bool):
 self_bonding_allowed (bool):
 atomic_radius_data (str):
 Defines which column of :attr:`constants.elements` is used. The default is ``atomic_radius_cc`` and can be changed with :attr:`settings['defaults']['atomic_radius_data']`. Compare with :func:`add_data`. Returns: dict: Dictionary mapping from an atom index to the set of indices of atoms bonded to. """

        if atomic_radius_data is None:
            atomic_radius_data = settings['defaults']['atomic_radius_data']

        def complete_calculation():
            old_index = self.index
            self.index = range(len(self))
            fragments = self._divide_et_impera(offset=offset)
            positions = np.array(self.loc[:, ['x', 'y', 'z']], order='F')
            data = self.add_data([atomic_radius_data, 'valency'])
            bond_radii = data[atomic_radius_data]
            if modified_properties is not None:
                bond_radii.update(pd.Series(modified_properties))
            bond_radii = bond_radii.values
            bond_dict = collections.defaultdict(set)
            for i, j, k in product(*[range(x) for x in fragments.shape]):
                # The following call is not side effect free and changes
                # bond_dict
                self._update_bond_dict(
                    fragments[i, j, k], positions, bond_radii,
                    bond_dict=bond_dict,
                    self_bonding_allowed=self_bonding_allowed)

            for i in set(self.index) - set(bond_dict.keys()):
                bond_dict[i] = {}

            self.index = old_index
            rename = dict(enumerate(self.index))
            bond_dict = {rename[key]: {rename[i] for i in bond_dict[key]}
                         for key in bond_dict}
            return bond_dict

        if use_lookup:
            try:
                bond_dict = self._metadata['bond_dict']
            except KeyError:
                bond_dict = complete_calculation()
        else:
            bond_dict = complete_calculation()

        if set_lookup:
            self._metadata['bond_dict'] = bond_dict
        return bond_dict