<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def props(self, element=None, mode='all', deep=False):
 r""" Returns a list containing the names of all defined pore or throat properties. Parameters element : string, optional Can be either 'pore' or 'throat' to specify what properties are returned. If no element is given, both are returned mode : string, optional Controls what type of properties are returned. Options are: **'all'** : Returns all properties on the object (default) **'models'** : Returns only properties that are associated with a model **'constants'** : returns data values that were *not* generated by a model, but manaully created. deep : boolean If ``True`` this will also return the data on any associated subdomain objects Returns ------- A an alphabetically sorted list containing the string name of all pore or throat properties currently defined. This list is an iterable, so is useful for scanning through properties. See Also -------- labels keys Examples -------- ['pore.coords'] ['throat.conns'] ['pore.coords', 'throat.conns'] """

        # Parse Inputs
        element = self._parse_element(element=element)
        allowed_modes = ['all', 'constants', 'models']
        mode = self._parse_mode(mode=mode, allowed=allowed_modes, single=True)
        if mode == 'all':
            vals = set(self.keys(mode='props'))
        if mode == 'constants':
            if hasattr(self, 'models'):
                temp = set(self.keys(mode='props'))
                vals = temp.difference(self.models.keys())
            else:
                vals = set(self.keys(mode='props'))
        if mode == 'models':
            if hasattr(self, 'models'):
                temp = set(self.keys(mode='props'))
                vals = temp.intersection(self.models.keys())
            else:
                logger.warning('Object does not have a models attribute')
                vals = set()
        # Deal with hidden props
        hide = set([i for i in self.keys() if i.split('.')[1].startswith('_')])
        vals = vals.difference(hide)
        # Remove values of the wrong element
        temp = set([i for i in vals if i.split('.')[0] not in element])
        vals = set(vals).difference(temp)
        # Convert to nice list for printing
        vals = PrintableList(list(vals))
        # Repeat for associated objects if deep is True
        if deep:
            if self._isa('phase'):
                for item in self.project.find_physics(phase=self):
                    vals += item.props(element=element, mode=mode, deep=False)
            if self._isa('network'):
                for item in self.project.geometries().values():
                    vals += item.props(element=element, mode=mode, deep=False)
        return vals