Получение карты () для возврата списка в Python 3.x

523

Я пытаюсь отобразить список в гекс, а затем использовать список в другом месте. В Python 2.6 это было легко:

A: Python 2.6:

>>> map(chr, [66, 53, 0, 94])
['B', '5', '\x00', '^']

Однако в Python 3.1 вышеприведенное возвращает объект карты.

B: Python 3.1:

>>> map(chr, [66, 53, 0, 94])
<map object at 0x00AF5570>

Как мне получить сопоставленный список (как в A выше) на Python 3.x?

В качестве альтернативы, есть ли лучший способ сделать это? Мой начальный объект списка содержит около 45 элементов, и я хотел бы преобразовать их в шестнадцатеричные.

mozami
источник
2
Это более питонно, чтобы использовать понимание списка . map()был почти удален из языка, потому что нет причин использовать его для понимания списка или forцикла.
Борис

Ответы:

771

Сделай это:

list(map(chr,[66,53,0,94]))

В Python 3+ многие процессы, которые выполняют итерации, возвращают сами итераторы. В большинстве случаев это приводит к экономии памяти и должно ускорить процесс.

Если все, что вы собираетесь сделать, это в конечном итоге перебрать этот список, нет необходимости даже конвертировать его в список, потому что вы все еще можете перебирать mapобъект следующим образом:

# Prints "ABCD"
for ch in map(chr,[65,66,67,68]):
    print(ch)
Триптих
источник
15
Конечно, вы можете повторить и это: (chr (x) для x в [65,66,67,68]). Ему даже не нужна карта.
Hughdbrown
2
@hughdbrown Аргументом для использования 3.1 mapбудет ленивая оценка при итерации сложной функции, больших наборов данных или потоков.
Эндрю Китон
18
@ Эндрю, на самом деле, Хью работает с генератором, который сделал бы то же самое. Обратите внимание на круглые скобки, а не на квадратные скобки.
Триптих
5
Альтернативное решение (более быстрое и для больших входных данных), когда известны значения ASCII / latin-1, заключается в массовом преобразовании на уровне C: bytes(sequence_of_ints_in_range_0_to_256).decode('latin-1')что strускоряет процесс , избегая вызовов функций Python для каждого элемента в пользу массового преобразования все элементы, использующие только вызовы функций уровня C. Вы можете обернуть вышеупомянутое, listесли вам действительно нужен listотдельный символ, но так strкак он уже итерируем из своих собственных символов, единственная причина, по которой вы это сделаете, это если вам нужна изменчивость.
ShadowRanger
1
list (map (str, [1,2,3])) выдает «Ошибка в аргументе» для Python 3.4.3 в CentOS 7. Работает со списком.
Андор
108

Новое и аккуратное в Python 3.5:

[*map(chr, [66, 53, 0, 94])]

Благодаря дополнительным обобщениям распаковки

ОБНОВИТЬ

Всегда ища более короткие пути, я обнаружил, что этот тоже работает:

*map(chr, [66, 53, 0, 94]),

Распаковка тоже работает в кортежах. Обратите внимание на запятую в конце. Это делает его кортежем из 1 элемента. То есть это эквивалентно(*map(chr, [66, 53, 0, 94]),)

Он короче всего на один символ из версии со списочными скобками, но, на мой взгляд, лучше написать, потому что вы начинаете прямо со звездочки - синтаксиса расширения, так что я чувствую, что это мягче на уме. :)

Израиль Унтерман
источник
11
@Quelklef list()не выглядит опрятно
Arijoon
5
@Quelklef: Кроме того, распаковка происходит быстрее, благодаря отсутствию необходимости искать listконструктор и вызывать механизм вызова общих функций. Для длинного ввода это не имеет значения; для короткого, это может иметь большое значение. Используя приведенный выше код с вводом в качестве такового, tupleчтобы он не восстанавливался многократно, ipythonмикробенчмарки показывают, что list()подход обертки занимает примерно на 20% больше времени, чем распаковка. Имейте в виду, в абсолютном выражении мы говорим о 150 нс, что тривиально, но вы поняли идею.
ShadowRanger
Что не так со старым map? Может быть, с новым именем ( lmap?), Если по умолчанию будет возвращаться итератор?
Джорджио
1
*map()дает ошибку синтаксиса на Python 3.6: can't use starred expression here. Вы должны поместить это в list:[ *map() ]
ALH
4
@ALH Вы пропустили запятую в конце команды. Легкая ошибка!
LondonRob
103

Почему ты не делаешь это:

[chr(x) for x in [66,53,0,94]]

Это называется пониманием списка. Вы можете найти много информации в Google, но вот ссылка на документацию по Python (2.6) по спискам . Возможно, вас больше заинтересует документация по Python 3 .

Марк Рушаков
источник
4
Хммм. Может быть, нужна общая публикация по спискам, генераторам, map (), zip () и многим другим качествам быстрой итерации в python.
hughdbrown
46
Я думаю, потому что это более многословно, вы должны написать дополнительную переменную (дважды) ... Если операция более сложная и вы заканчиваете тем, что пишете лямбду, или вам также нужно отбросить некоторые элементы, я думаю, что понимание определенно лучше чем карта + фильтр, но если у вас уже есть функция, которую вы хотите применить, карта более лаконична.
Фортран
1
+1: легче читать и позволяет использовать функции с множеством параметров
Le Droid
7
map(chr, [66,53,0,94])определенно более кратким, чем [chr(x) for x in [66,53,0,94]].
Джорджио
намного быстрее, чем другие ответы
Evhz
25

Функция карты, возвращающая список, имеет преимущество сохранения набора текста, особенно во время интерактивных сеансов. Вы можете определить lmapфункцию (по аналогии с python2 imap), которая возвращает список:

lmap = lambda func, *iterable: list(map(func, *iterable))

Тогда вызов lmapвместо того, mapчтобы сделать работу: lmap(str, x)короче на 5 символов (30% в этом случае), чем list(map(str, x))и, безусловно, короче, чем [str(v) for v in x]. Вы можете создать аналогичные функции для filterтоже.

Был комментарий к оригинальному вопросу:

Я бы предложил переименовать в Getting map (), чтобы получить список в Python 3. *, так как он применяется ко всем версиям Python3. Есть ли способ сделать это? - Meawoppl 24 января в 17:58

Это есть возможность сделать это, но это очень плохая идея. Просто для забавы, вот как вы можете ( но не должны ) делать это:

__global_map = map #keep reference to the original map
lmap = lambda func, *iterable: list(__global_map(func, *iterable)) # using "map" here will cause infinite recursion
map = lmap
x = [1, 2, 3]
map(str, x) #test
map = __global_map #restore the original map and don't do that again
map(str, x) #iterator
Борис Горелик
источник
4

Преобразование моего старого комментария для лучшей наглядности: для «лучшего способа сделать это» mapполностью, если известно, что ваши входные данные являются ординалами ASCII, обычно намного быстрее конвертировать bytesи декодировать, а-ля bytes(list_of_ordinals).decode('ascii'). Это дает вам одно strиз значений, но если вам нужна listизменчивость или тому подобное, вы можете просто преобразовать ее (и она все еще быстрее). Например, в ipythonмикробенчмарках, конвертирующих 45 входов:

>>> %%timeit -r5 ordinals = list(range(45))
... list(map(chr, ordinals))
...
3.91 µs ± 60.2 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... [*map(chr, ordinals)]
...
3.84 µs ± 219 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... [*bytes(ordinals).decode('ascii')]
...
1.43 µs ± 49.7 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

>>> %%timeit -r5 ordinals = list(range(45))
... bytes(ordinals).decode('ascii')
...
781 ns ± 15.9 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

Если вы оставите его как str, это займет ~ 20% времени самых быстрых mapрешений; даже если вернуться к списку, это все равно менее 40% самого быстрого mapрешения. Массовое преобразование с помощью массового преобразования bytesи bytes.decodeпоследующее массовое преобразование обратно в listэкономит много работы, но, как уже было отмечено, работает, только если все ваши входные данные являются порядковыми числами ASCII (или порядковыми числами в несколько байтов на кодировку, специфичную для локали, например latin-1).

ShadowRanger
источник
2
list(map(chr, [66, 53, 0, 94]))

map (func, * iterables) -> объект карты Создайте итератор, который вычисляет функцию, используя аргументы из каждого из итерируемых элементов. Останавливается, когда самая короткая итерация исчерпана.

«Сделай итератор»

означает, что он вернет итератор.

«который вычисляет функцию, используя аргументы из каждого итераций»

означает, что функция next () итератора будет принимать одно значение каждого итерируемого элемента и передавать каждое из них одному позиционному параметру функции.

Таким образом, вы получаете итератор из функции map () и передаете его во встроенную функцию list () или используете списки.

Ини
источник
2

В дополнении к выше ответам в Python 3, мы можем просто создать listзначения результата из , mapкак

li = []
for x in map(chr,[66,53,0,94]):
    li.append(x)

print (li)
>>>['B', '5', '\x00', '^']

Мы можем обобщить на другом примере, где меня поразили, операции с картой также можно обрабатывать аналогично, как в regexзадаче, мы можем написать функцию для получения listэлементов для отображения и получения набора результатов одновременно. Ex.

b = 'Strings: 1,072, Another String: 474 '
li = []
for x in map(int,map(int, re.findall('\d+', b))):
    li.append(x)

print (li)
>>>[1, 72, 474]
Harry_pb
источник
@miradulo Я предполагал, что в Python 2 был возвращен список, но в Python 3 возвращается только тип, и я просто попытался дать в том же формате. Если вы считаете это ненужным, возможно, есть такие люди, как я, которые могут найти это полезным, и именно поэтому я добавил.
Harry_pb
2
Когда уже есть понимание списка, функция списка и ответ распаковки, явный цикл for не добавляет много IMHO.
Мирадуло
1

Вы можете попробовать получить список из объекта карты, просто перебирая каждый элемент в объекте и сохраняя его в другой переменной.

a = map(chr, [66, 53, 0, 94])
b = [item for item in a]
print(b)
>>>['B', '5', '\x00', '^']
Акшай Сатпаиз
источник
0

Используя понимание списка в python и утилиту базовой функции map, можно также сделать это:

chi = [x for x in map(chr,[66,53,0,94])]

даршан кс
источник
список будет содержать значение ASIC данных элементов.
даршан кс