2017-02-07 13 views
9

Mam dowolnie zagnieżdżone iterable tak:Python: Mapa funkcję nad rekurencyjnych iterables

numbers = (1, 2, (3, (4, 5)), 7) 

i chciałbym mapować funkcję nad nim bez zmiany struktury. Na przykład, może chciałbym konwertować wszystkie numery na ciągi, aby uzyskać

strings = recursive_map(str, numbers) 
assert strings == ('1', '2', ('3', ('4', '5')), '7') 

Czy jest to dobry sposób to zrobić? Mogę zobrazować napisanie własnej metody ręcznego przemierzania numbers, ale chciałbym się dowiedzieć, czy istnieje ogólny sposób mapowania rekursywnych iterables.

Ponadto, w moim przykładzie, jest w porządku, jeśli strings daje mi zagnieżdżonych list (lub niektórych iterable) raczej zagnieżdżonych krotek.

Odpowiedz

10

Mamy skanować każdy element sekwencji, i przechodzi do głębszej rekursji, jeśli bieżąca pozycja jest pod-sekwencja, czy daje to mapowanie jeśli doszliśmy do danych typu non-sekwencji (może być int, str lub dowolny kompleks klasy).

Używamy collections.Sequence do generalizowania pomysłu na każdą sekwencję, a nie tylko krotek lub list, i type(item) po wydajności, aby zapewnić, że pod-sekwencje, które otrzymamy, pozostają takie same, jakimi były.

from collections import Sequence 

def recursive_map (seq, func): 
    for item in seq: 
     if isinstance(item, Sequence): 
      yield type(item)(recursive_map(item, func)) 
     else: 
      yield func(item) 

Demo:

>>> numbers = (1, 2, (3, (4, 5)), 7) 
>>> mapped = recursive_map(numbers, str) 
>>> tuple(mapped) 
('1', '2', ('3', ('4', '5')), '7') 

lub bardziej skomplikowany przykład:

>>> complex_list = (1, 2, [3, (complex('4+2j'), 5)], map(str, (range(7, 10)))) 
>>> tuple(recursive_map(complex_list, lambda x: x.__class__.__name__)) 
('int', 'int', ['int', ('complex', 'int')], 'map') 
+1

Proszę wyjaśnić swój kod. –

+0

Sądzę, że generalizujesz tylko * sekwencje *. To, czy coś jest iterowalne, nie jest problemem typu, ale podąża za protokołem. OP może oznaczać sekwencję, ale nie spowoduje to iteracji po zestawach, kanałach itd. To, czy coś jest iterowalne, jest zdefiniowane przez to, czy implementuje on protokół iterowalny –

+0

@ juanpa.arrivillaga dzięki! zaktualizowana semantyka – Uriel

6
def recursive_map(f, it): 
    return (recursive_map(f, x) if isinstance(x, tuple) else f(x) for x in it) 
0

Jeśli chcesz przedłużyć swój wynik dict, set i innych, można użyć Uriel użytkownika odpowiedź:

Wejście

Testy:

recursive_map({0: [1, {2, 2, 3}]}, str) 

Plony:

{0: ['1', '{2, 3}']} 
Powiązane problemy