It’s hard to make something up - everything seems to be in your solution. However, you can try to make a couple (unnecessary?) Changes + function in one line. A large function can also be placed in 1 line, if desired, but this will already be too:
import timeit def map_nested(lst, func=lambda x: x, forbidden_types=(str, int)): container_type = type(lst) if hasattr(lst, '__iter__') and type(lst) not in forbidden_types: return container_type(map_nested(item, func) for item in lst) else: try: return func(lst) except: return lst def map_nested_1line(lst, func=lambda x: x): return [map_nested_1line(item, func) for item in lst] if type(lst) is list else func(lst)
UPDATE: Morning is wiser than the evening and after thinking I gave birth to a non-recursive version of this function. Non-recursive is good because it does not fall on long and heavily nested lists on the OS with a limit on the length of the recursion . Bad that very slow. The essence of a non-recursive solution is that the order of entry and exit into nested lists is entered into a special array. We find the nested array - we enter a pointer to it + index in the spec. repository. We leave from it - the pointer and the index is retrieved.
# Also non-recursive. Yay! def map_nested_inplace(lst, func=lambda x: x, forbidden_types=(str, int)): # forbidden_types не используется processed_elements = 0 # assert type(lst) is list # blah-blah current_container = lst containers_repo = [[current_container, 0]] # До тех пор, пока не были обработаны все списки и под-списки while len(containers_repo) != 0: while True: try: # Следующий элемент в текущем списке. # Может быть как числом, так и новым под-списком next_one = current_container[containers_repo[-1][1]] break # Исключение означает, что под-список кончился. # Т.к. он кончился, то убираем его из хранилища # и пробуем извлечь следующий элемент except IndexError: containers_repo.pop() if len(containers_repo) == 0: break current_container = containers_repo[-1][0] if len(containers_repo) != 0: if type(next_one) is list: # Это под-список, а не число. # Заносим под-список в хранилище и # на следующей итерации открываем уже его containers_repo[-1][1] += 1 current_container = next_one containers_repo.append([current_container, 0]) else: current_container[containers_repo[-1][1]] = func(current_container[containers_repo[-1][1]]) containers_repo[-1][1] += 1 # ради небольшой проверки processed_elements += 1 # set также работает, но непохоже, чтобы он сохранял порядок lst = ['1', 'an error', ('1', ['2', '4', (5, '6')]), '7', 8] lst_simple = ['1', '2', ['1', ['2', '4', ['5', '6']]], '7', '8'] lst_another_simple = ['1', '2', ['1', ['2', '4', ['5', '6']], ['6', '6', '6'], ['8', '8']], '7', ['11'], '8'] print(map_nested(lst, lambda x: int(x)**2)) print(map_nested_1line(lst_simple, lambda x: int(x)**2)) print(map_nested_1line([], lambda x: int(x)**2)) map_nested_inplace(lst_another_simple, lambda x: int(x)**2) print(lst_another_simple)
Also a bit of testing:
import random test_array = [] container_tree = [test_array] current_container = container_tree[-1] TOTAL_AMOUNT = 10000 NEW_LEVEL_PROBABILITY = 0.5 for i in range(TOTAL_AMOUNT): if random.random() >= NEW_LEVEL_PROBABILITY: current_container.append([]) container_tree.append(current_container[-1]) current_container = current_container[-1] elif len(container_tree) > 1: current_container = container_tree[-2] current_container.append(str(random.randint(0, 20))) # Для работоспособности рекурсивных методов # Впрочем, без старта новго потока с threading.stack_size(<Big value>) все равно не будет работать :( import sys if sys.getrecursionlimit() < len(container_tree) * 2: sys.setrecursionlimit(len(container_tree) * 2) setup_statement = """from __main__ import test_array, """ # Не используется lambda x**2, потому что # в inplace квадраты будут накатываться до тех пор, # пока хватит памяти - список для каждого прохода должен генерироваться заново print(timeit.timeit("map_nested_inplace(test_array)", setup=setup_statement + "map_nested_inplace", number=100)) print(timeit.timeit("map_nested_1line(test_array)", setup=setup_statement + "map_nested_1line", number=100)) print(timeit.timeit("map_nested(test_array)", setup=setup_statement + "map_nested", number=100)) >>> 1.8096923486838081 # Без рекурсии >>> 0.9477624593712808 # Однострочник >>> 1.827232542223693 # Большая функция