Obtendo um map () para retornar uma lista no Python 3.x

523

Estou tentando mapear uma lista em hexadecimal e depois usá-la em outro lugar. No python 2.6, isso foi fácil:

A: Python 2.6:

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

No entanto, no Python 3.1, o exemplo acima retorna um objeto de mapa.

B: Python 3.1:

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

Como recupero a lista mapeada (como em A acima) no Python 3.x?

Como alternativa, existe uma maneira melhor de fazer isso? Meu objeto de lista inicial tem cerca de 45 itens e gostaria de convertê-los em hexadecimal.

mozami
fonte
2
É mais pitônico usar uma compreensão de lista . map()foi quase removido do idioma porque não há motivo para usá-lo em uma compreensão de lista ou em um forloop.
Boris

Respostas:

771

Faça isso:

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

No Python 3+, muitos processos que iteram sobre iterables retornam os próprios iteradores. Na maioria dos casos, isso acaba economizando memória e deve acelerar as coisas.

Se tudo o que você fizer é percorrer esta lista eventualmente, não será necessário convertê-la em uma lista, porque você ainda pode percorrer o mapobjeto da seguinte maneira:

# Prints "ABCD"
for ch in map(chr,[65,66,67,68]):
    print(ch)
Tríptico
fonte
15
Obviamente, você também pode iterar: (chr (x) para x em [65,66,67,68]). Nem precisa de mapa.
hughdbrown 20/08/09
2
@hughdbrown O argumento para o uso de 3.1 mapseria uma avaliação lenta ao iterar em uma função complexa, grandes conjuntos de dados ou fluxos.
18119 Andrew Keeton
18
@ Andrew, na verdade, Hugh está usando uma compreensão de gerador que faria a mesma coisa. Observe os parênteses em vez de colchetes.
Triptych
5
Solução alternativa (mais rápida também para entradas grandes) quando os valores são conhecidos como ASCII / latin-1 é fazer conversões em massa na camada C: o bytes(sequence_of_ints_in_range_0_to_256).decode('latin-1')que torna strmais rápido, evitando chamadas de função Python para cada elemento em favor de uma conversão em massa de todos os elementos usando apenas chamadas de função no nível C. Você pode incluir o que foi dito acima listse realmente precisar de um listdos personagens individuais, mas como strjá é iterável por seus próprios personagens, a única razão pela qual você faria isso é se precisar de mutabilidade.
ShadowRanger
1
list (map (str, [1,2,3])) fornece "Erro no argumento" para Python 3.4.3 no CentOS 7. A compreensão da lista funciona.
Andor
108

Novo e arrumado no Python 3.5:

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

Graças a generalizações adicionais de desempacotamento

ATUALIZAR

Sempre procurando caminhos mais curtos, descobri que este também funciona:

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

A descompactação também funciona em tuplas. Observe a vírgula no final. Isso o torna uma tupla de 1 elemento. Ou seja, é equivalente a(*map(chr, [66, 53, 0, 94]),)

É mais curto em apenas um caractere da versão com os colchetes da lista, mas, na minha opinião, é melhor escrever, porque você começa com o asterisco - a sintaxe de expansão, então acho que é mais suave. :)

Israel Unterman
fonte
11
@Quelklef list()não parece tão puro
Arijoon
5
@Quelklef: Além disso, a abordagem de descompactação é trivialmente mais rápida, graças à não necessidade de procurar o listconstrutor e invocar o mecanismo de chamada de função geral. Para uma entrada longa, não importa; para um curto, pode fazer uma grande diferença. Usando o código acima com a entrada como uma tupleforma que não seja reconstruída repetidamente, as ipythonmarcas de microparque mostram que a list()abordagem de empacotamento leva cerca de 20% mais tempo que a descompactação. Lembre-se, em termos absolutos, estamos falando de 150 ns, o que é trivial, mas você entendeu.
ShadowRanger 8/17
O que havia de errado com o velho map? Talvez com um novo nome ( lmap?), Se o novo padrão é retornar um iterador?
Giorgio
1
*map()dá erro de sintaxe em Python 3.6: can't use starred expression here. Você precisa colocá-lo em um list:[ *map() ]
ALH 22/12
4
@ ALH Você perdeu a vírgula no final do comando. Erro fácil de cometer!
precisa saber é o seguinte
103

Por que você não está fazendo isso:

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

Isso se chama compreensão de lista. Você pode encontrar muitas informações no Google, mas aqui está o link para a documentação do Python (2.6) sobre compreensão de lista . Você pode estar mais interessado na documentação do Python 3 .

Mark Rushakoff
fonte
4
Hmmmm. Talvez seja necessário haver uma postagem geral sobre compreensões de lista, geradores, map (), zip () e muitas outras vantagens rápidas de iteração em python.
hughdbrown 20/08/09
46
Eu acho que porque é mais detalhado, você precisa escrever uma variável extra (duas vezes) ... Se a operação for mais complexa e você acabar escrevendo um lambda, ou você também precisar soltar alguns elementos, acho que uma compreensão é definitivamente melhor do que um mapa + filtro, mas se você já possui a função que deseja aplicar, o mapa é mais sucinto.
fortran
1
+1: Mais fácil de ler e permite que você use funções com muitos parâmetros
Le Droid
7
map(chr, [66,53,0,94])é definitivamente mais conciso do que [chr(x) for x in [66,53,0,94]].
Giorgio
muito mais rápido que as outras respostas
Evhz
25

A função de mapa de retorno de lista tem a vantagem de salvar a digitação, especialmente durante as sessões interativas. Você pode definir a lmapfunção (na analogia do python2 imap) que retorna a lista:

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

A chamada, em lmapvez de map, fará o trabalho: lmap(str, x)é menor em 5 caracteres (30% nesse caso) list(map(str, x))e certamente menor que [str(v) for v in x]. Você também pode criar funções semelhantes filter.

Houve um comentário à pergunta original:

Eu sugeriria uma renomeação para Getting map () para retornar uma lista no Python 3. *, como se aplica a todas as versões do Python3. Existe uma maneira de fazer isso? - meawoppl 24/01 às 17:58

Ele é possível fazer isso, mas é uma idéia muito ruim. Apenas por diversão, veja como você pode ( mas não deve ) fazê-lo:

__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
Boris Gorelik
fonte
4

Convertendo meu comentário antigo para melhor visibilidade: para uma "melhor maneira de fazer isso" sem mapinteiramente, se suas entradas são conhecidas como ordinais ASCII, geralmente é muito mais rápido converter bytese decodificar a la bytes(list_of_ordinals).decode('ascii'). Isso dá a você um strdos valores, mas se você precisar de um listpara mutabilidade ou algo semelhante, basta convertê-lo (e ainda é mais rápido). Por exemplo, em ipythonmarcas de micropontos convertendo 45 entradas:

>>> %%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)

Se você deixar como str, leva aproximadamente 20% do tempo das mapsoluções mais rápidas ; mesmo convertendo de volta à lista, ainda é menos de 40% da mapsolução mais rápida . A conversão em massa via bytese, em bytes.decodeseguida, a conversão em massa de volta para listeconomizar muito trabalho, mas , como observado, só funciona se todas as suas entradas forem ordinais ASCII (ou ordinais em algum byte por codificação específica de local do caractere, por exemplo latin-1).

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

map (func, * iterables) -> objeto de mapa Faça um iterador que calcule a função usando argumentos de cada um dos iterables. Para quando o iterável mais curto estiver esgotado.

"Faça um iterador"

significa que ele retornará um iterador.

"que calcula a função usando argumentos de cada um dos iterables"

significa que a próxima função () do iterador pegará um valor de cada iterável e passará cada um deles para um parâmetro posicional da função.

Então você obtém um iterador da função map () e passa-o para a função incorporada list () ou usa a compreensão da lista.

Ini
fonte
2

Além das respostas acima Python 3, podemos simplesmente criar um listdos valores de resultado de a mapas

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

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

Podemos generalizar por outro exemplo em que fui atingido; as operações no mapa também podem ser tratadas de maneira semelhante, como no regexproblema; podemos escrever funções para obter listitens para mapear e obter resultados ao mesmo tempo. 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
fonte
@miradulo Eu suponho que no Python 2, uma lista foi retornada, mas no Python 3, apenas o tipo é retornado e eu apenas tentei dar no mesmo formato. Se você acha que é desnecessário, talvez haja pessoas como eu que possam achar útil e foi por isso que adicionei.
Harry_pb
2
Quando já existe uma compreensão da lista, uma função de lista e uma resposta descompactada, um loop for explícito não adiciona muito IMHO.
Miradulo 15/06
1

Você pode tentar obter uma lista do objeto de mapa apenas iterando cada item no objeto e armazená-lo em uma variável diferente.

a = map(chr, [66, 53, 0, 94])
b = [item for item in a]
print(b)
>>>['B', '5', '\x00', '^']
Akshay Satpaise
fonte
0

Usando a compreensão de lista no python e no utilitário básico de função de mapa, também é possível fazer isso:

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

darshan ks
fonte
A lista chi conterá o valor ASIC dos elementos fornecidos.
precisa