Python: как да мутирам подмножество от речникови стойности?

Имам вече създаден речник d. Искам да променя само стойностите от определен тип. Например, искам да заменя низовете с техните дължини.

Скучният начин да направите това е с for цикъл:

for k,v in d.items():
    if isinstance(v, str):
        d[k] = len(v)

Но зациклянето не е ли бавно?
Може да се направи с разбиране. Мисля, че това е еквивалентно на горното:

d = {k: len(v) if isinstance(v, str) else v for k,v in d.items()}

Опитах го с карта, но те не работят (очевидно защото не разбирам нещо за разопаковането на кортежи в Python 3):

d = dict(map(lambda k,v: (k, len(v) if isinstance(v, str) else v), d.items()))
d = dict(map(lambda (k,v): (k, len(v) if isinstance(v, str) else v), d.items()))

Това изглежда работи, но става голямо и грозно:

dict(map(lambda kv: (kv[0], len(kv[1]) if isinstance(kv[1], str) else kv[1]), d.items()))

Изглежда, че този вид операция би била често срещана, но не мога да намеря конкретни отговори.
Какъв е правилният, Pythonic и производителен начин да го направите?


person Jacktose    schedule 09.03.2018    source източник
comment
Вашето dict разбиране е правилно и imho е питоничният начин да постигнете това.   -  person Cory Kramer    schedule 10.03.2018
comment
Цикълът for не създава нов речник и замества само онези елементи, които имат низ като стойност. Разбирането създава нов речник и задава всеки елемент, включително тези, чиято стойност не е низ. Питате дали цикълът е бавен, но когато трябва да се справите с всеки елемент в набор от стойности, няма алтернатива. Разбирането не е магия; също е цикъл.   -  person Paul Cornelius    schedule 10.03.2018
comment
@PaulCornelius Ауу, не магия? Не, просто си помислих, че може би е по-оптимизирано зад кулисите. Така че цикълът for може би е по-бърз? Или използва по-малко памет?   -  person Jacktose    schedule 10.03.2018
comment
При равни други условия винаги бихте предпочели разбирането пред for цикъла. Но ако имате милион елемента в речника си и само два от тях са низове, разбирането ще изпълни 999 998 ненужни копия, докато for цикълът просто ще замени 2 стойности. Така че правило като comprehension=fast, for loop=slow е твърде просто.   -  person Paul Cornelius    schedule 10.03.2018
comment
@P1h3r1e3d13 for-цикълът определено ще използва по-малко памет в този случай, всъщност той използва постоянно количество памет, срещу разбирането, което изисква O(n) памет. Но дори ако вашият цикъл е идеално еквивалентен на разбирането, т.е. ще изгради нов dict вместо да модифицира съществуващ, тогава можете да очаквате само незначителни печалби на скоростта. Разбирането трябва да се предпочита най-вече за четливост.   -  person juanpa.arrivillaga    schedule 10.03.2018
comment
IOW: for-циклите са Pythonic.   -  person juanpa.arrivillaga    schedule 10.03.2018


Отговори (1)


Вече имате отговора и CoryKramer вече го спомена.

d = {k: len(v) if isinstance(v, str) else v for k,v in d.items()}

Това го прави и е най-чистият начин.

person A. Stewart    schedule 09.03.2018