def prepare_function(phone_ranges):
    """
    >>> phone_ranges = [
    ...     {'def_code': '900', 'start': '0', 'end': '9', 'operator': 'MTS', 'region': 'MSK', 'region_id': 1},
    ...     {'def_code': '901', 'start': '1', 'end': '1', 'operator': 'TL2', 'region': 'SPB', 'region_id': 2},
    ... ]
    >>> get_data = prepare_function(phone_ranges)
    >>> get_data('79001')
    {'operator': 'MTS', 'region': 'MSK', 'region_id': 1}
    >>> get_data('79011')
    {'operator': 'TL2', 'region': 'SPB', 'region_id': 2}
    >>> get_data('79012')
    {}
    """

    ranges = sorted([
        {
            'start': int('7{}{}'.format(rng['def_code'], rng['start'])),
            'end': int('7{}{}'.format(rng['def_code'], rng['end'])),
            'data': {
                'operator': rng['operator'],
                'region': rng['region'],
                'region_id': rng['region_id'],
            }
        }
        for rng in phone_ranges
    ], key=lambda x: x['start'])

    assert ranges, 'Ranges should be not empty'
    assert all(r['start'] <= r['end'] for r in ranges),\
        'Not valid ranges (start > end)'
    assert all(r['end'] < next_r['start'] for r, next_r in zip(ranges, ranges[1:])),\
        'Not valid ranges (end of the current range >= start of the next range)'

    def get_data(phone):
        if not phone:
            return {}
        phone_int = int(phone)

        low, high = 0, len(ranges) - 1
        if not ranges[low]['start'] <= phone_int <= ranges[high]['end']:
            return {}

        while low + 1 < high:
            mid = (low + high) // 2

            if ranges[mid]['start'] <= phone_int:
                low = mid
            else:
                high = mid

        if phone_int <= ranges[low]['end']:
            return ranges[low]['data']
        if ranges[high]['start'] <= phone_int:
            return ranges[high]['data']
        return {}

    return get_data


class PhoneRangeReducer(object):

    def __init__(self, phone_ranges_info, ext_id_key):
        self.get_data = prepare_function(phone_ranges_info)
        self.ext_id_key = ext_id_key

    def __call__(self, key, recs):
        data = {}
        for rec in recs:
            if rec.get('phone'):
                data = self.get_data(rec.get('phone'))
                if data:
                    break

        yield {
            self.ext_id_key: key[self.ext_id_key],
            'operator': data.get('operator', None),
            'region': data.get('region', None),
            'region_id': data.get('region_id', None)
        }
