def sort_it(obj=None, sort_order=None):
    """Sort a composite python object.
    :param obj: Python object
    :param sort_order: optional custom sort order
    :rtype: OrderedDict
    :returns: Sorted composite object.
    """
text_types = str, int, float, complex
iterable_types = list, tuple, set, frozenset

def cmp_func(a, b):
    """Function passed as `cmp` arg to sorted method
        Basic Algorithm
        - text_types take precedence over non text_types
        - Mapping types take precedence over iterable container types
        - Values of the same (or similar) type:
           - if sort_order is defined
              - if both keys are in sort order, the key index position determines precedence
              - if only one of the keys are in sort order then it takes precedence
              - if neither keys are in sort_order their lexicographic order is the determinant
           - otherwise, fall back to lexicographic ordering
        :param a: first arg passed to sorted's cmp arg
        :param b: second arg passed to sorted's cmp arg
        :rtype: int
        :return: int to determine which object (a/b) should take precedence
        """
if not any([len(a) == 2, len(b) == 2]):
    return 0
elif isinstance(a[1], text_types) and not isinstance(b[1], text_types):
    return -1
elif not isinstance(a[1], text_types) and isinstance(b[1], text_types):
    return 1
elif isinstance(a[1], collections.Mapping) and isinstance(b[1], iterable_types
    ):
    return -1
elif isinstance(b[1], collections.Mapping) and isinstance(a[1], iterable_types
    ):
    return 1
elif any([isinstance(a[1], text_types) and isinstance(b[1], text_types), 
    isinstance(a[1], iterable_types) and isinstance(b[1], iterable_types), 
    isinstance(a[1], collections.Mapping) and isinstance(b[1], collections.
    Mapping), isinstance(a[1], type(b[1]))]):
    if sort_order:
        if any([a[0] in sort_order, b[0] in sort_order]):
            if a[0] in sort_order and b[0] not in sort_order:
                return -1
if b[0] in sort_order and a[0] not in sort_order:
    return 1
if a[0] in sort_order and b[0] in sort_order:
    if sort_order.index(a[0]) > sort_order.index(b[0]):
        return 1
else:
    return -1
if sorted([a[0].lower(), b[0].lower()]).index(a[0].lower()) == 0:
    return -1
elif sorted([a[0].lower(), b[0].lower()]).index(a[0].lower()) == 1:
    return 1
else:
    raise ValueError('Unhandled condition for values %s, %s' % (a, b))
if isinstance(obj, collections.Mapping):
    return collections.OrderedDict((key, sort_it(value, sort_order=
        sort_order)) for key, value in sorted(list(obj.items()), key=cmp_to_key(
        cmp_func)))
elif isinstance(obj, iterable_types):
    return type(obj)([sort_it(value, sort_order=sort_order) for value in obj])
else:
    return obj
sort_order = ['id', 'rn', 'dn', 'vendor', 'model', 'serial', 'name',
    'description', 'traypresence']
sample_data_uri = 'https://bit.ly/1jOpQF2'
print(json.dumps(sort_it(json.loads(urllib.request.urlopen(sample_data_uri).read()
    )), indent=4))
print(json.dumps(sort_it(json.loads(urllib.request.urlopen(sample_data_uri).read()
    ), sort_order=sort_order), indent=4))
python — сложная сортировка вложенных данных