Eu li numa doc[0], que é possível ordenar listas de dicts desse modo:
>>> from operator import itemgetter
>>> result = sorted(undecorated, key=itemgetter('key2'))
Eu preciso ordernar uma lista, se baseando em 2 ou mais chaves, então
fiz:
>>> result = sorted(undecorated, key=itemgetter('key2', 'key3'))
Funcionou, mas eu percebi que há alguns campos acentuados e não são
ordenados corretamente. Eu vi nessa lista que você pode passar
'locale.strcoll' como argumento 'cmp' da função sorted. O problema é que
quando eu passo somente uma chave ( key=itemgetter('key2') ), funciona
mas quando passo mais de uma acontece o seguinte erro:
result = sorted(undecorated, cmp=locale.strcoll, key=itemgetter('key2',
'key3')
TypeError: coercing to Unicode: need string or buffer, tuple found
Por que isto não funciona? Ou existe um outro jeito para ordenar desse
jeito, com base em 2 ou mais chaves, e levando em consideração a
acentuação.
[0]http://wiki.python.org/moin/SortingListsOfDictionaries
Obrigado.
Paulo Eduardo Danker
_______________________________________________________
Yahoo! Mail - Sempre a melhor op��o para voc�!
Experimente j� e veja as novidades.
http://br.yahoo.com/mailbeta/tudonovo/
------------------------------------
,-----------------------------------------------------------.
| Antes de enviar um e-mail para o grupo leia: |
| http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar |
| E se você é usuário do BOL lembre-se de cadastrar o |
| e-mail do grupo na lista branca do seu sistema anti-spam. |
`-----------------------------------------------------------´Links do Yahoo! Grupos
<*> Para visitar o site do seu grupo na web, acesse:
http://br.groups.yahoo.com/group/python-brasil/
<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@yahoogrupos.com.br
<*> O uso que você faz do Yahoo! Grupos está sujeito aos:
http://br.yahoo.com/info/utos.html
A resposta está na mensagem de erro: porque a função locale.strcoll
espera uma string ou um buffer que possam ser convertidos em Unicode,
porém operator.itemgetter() retorna uma tupla sempre que você passa
mais do que um argumento.
Inclusive, não entendi porque você está usando o itemgetter com dois
argumentos... você pode mostrar um exemplo do conteúdo da sua
estrutura "undecorated"? Pelos valores de chaves "key1" e "key2" não
dá para imaginar o que tem nesta sua estrutura de dados...
> Ou existe um outro jeito para ordenar desse
> jeito, com base em 2 ou mais chaves, e levando em consideração a
> acentuação.
>
> [0]http://wiki.python.org/moin/SortingListsOfDictionaries
A forma clássica de ordenar com duas ou mais chaves é montar uma tupla
com as chaves, e ordenar por esta tupla. Mas sem entender como é a sua
estrutura de dados eu não consigo te mostrar exatamente como fazer.
[ ]s
Luciano
On Mon, Jul 7, 2008 at 4:43 PM, Paulo Eduardo Danker
<paulo_...@yahoo.com.br> wrote:
> Boa tarde,
>
> Eu li numa doc[0], que é possível ordenar listas de dicts desse modo:
[...]
> result = sorted(undecorated, cmp=locale.strcoll, key=itemgetter('key2',
> 'key3')
> TypeError: coercing to Unicode: need string or buffer, tuple found
>
> Por que isto não funciona? Ou existe um outro jeito para ordenar desse
[...]
Entender como as coisas funcionam é importante na hora de trocar o
achocolatado por chocolate em pó, ou a manteiga por margarina na
receita de bolo.
Nesse caso itemgetter('key2', 'key3') vai retornar uma tupla e
locale.strcoll não sabe lidar com tuplas. Veja melhor como os
atributos key e cmp do sorted (e list.sort) funcionam para entender.
Uma alternativa é usar:
result = sorted(undecorated, key=lambda x: (locale.strcoll(x['key2']),
locale.strcoll(x['key3'])))
Supondo que ambos os valores associados a 'key2' e 'key3' são strings,
se não forem daí você terá que fazer substituições. :)
Abraço,
-- Nilton
Em Seg, 2008-07-07 às 23:02 -0300, Nilton Volpato escreveu:
> Uma alternativa é usar:
> result = sorted(undecorated, key=lambda x: (locale.strcoll(x['key2']),
> locale.strcoll(x['key3'])))
>
> Supondo que ambos os valores associados a 'key2' e 'key3' são strings,
> se não forem daí você terá que fazer substituições. :)
>
> Abraço,
> -- Nilton
Eu testei isso mas não funciona, strcoll precisa de 2 argumentos, só
está recebendo um. Os 2 argumentos são strings para poder fazer o
comparação, certo? Então nesse caso qual seria o segundo argumento?
Paulo Eduardo Danker
_______________________________________________________
Yahoo! Mail - Sempre a melhor op��o para voc�!
Experimente j� e veja as novidades.
http://br.yahoo.com/mailbeta/tudonovo/
On Tue, Jul 8, 2008 at 11:59 AM, Paulo Eduardo Danker
<paulo_...@yahoo.com.br> wrote:
[...]
>> result = sorted(undecorated, key=lambda x: (locale.strcoll(x['key2']),
>> locale.strcoll(x['key3'])))
[...]
> Eu testei isso mas não funciona, strcoll precisa de 2 argumentos, só
> está recebendo um. Os 2 argumentos são strings para poder fazer o
> comparação, certo? Então nesse caso qual seria o segundo argumento?
É verdade, você pode usar o locale.strxfrm nesse caso, ao invés do
locale.strcoll. Veja a documentação do módulo locale. Não é tão
difícil encontrar isso lá.
Abraço,
-- Nilton
Nilton, não entendi para que serve locale.strxfrm. Fiquei com a
impressão de que é algum tipo de otimização apenas.
Tentei resolver o problema mencionado pelo Paulo com um exemplo aqui:
http://python.pastebin.com/m52a7d25a
Porém no meu Ubuntu 8.04 não estou conseguindo usar
locale.setlocale(locale.LC_ALL, 'pt_BR') pois levanta o erro de
'unsupported locale setting'.
Traceback (most recent call last):
File "multi_chaves.py", line 15, in <module>
locale.setlocale(locale.LC_ALL, 'pt_BR')
File "/usr/lib/python2.5/locale.py", line 478, in setlocale
return _setlocale(category, locale)
locale.Error: unsupported locale setting
Tenho os pacotes language-support-pt e language-pack-pt-base
insalados. O que mais falta para funcionar?
[ ]s
Luciano
Apenas completando, o problema pode ser visto na saída do último laço:
-----------------
Ordenando os itens por uf e nome:
PE Terezinha
SP Santos
SP Sarutaiá
SP São Paulo
TO Alvorada
-----------------
Por causa do acento, São Paulo ficou no lugar errado.
Deveria ser:
-----------------
Ordenando os itens por uf e nome:
PE Terezinha
SP Santos
SP São Paulo
SP Sarutaiá
TO Alvorada
-----------------
> Porém no meu Ubuntu 8.04 não estou conseguindo usar
> locale.setlocale(locale.LC_ALL, 'pt_BR') pois levanta o erro de
> 'unsupported locale setting'.
>
> Traceback (most recent call last):
> File "multi_chaves.py", line 15, in <module>
> locale.setlocale(locale.LC_ALL, 'pt_BR')
> File "/usr/lib/python2.5/locale.py", line 478, in setlocale
> return _setlocale(category, locale)
> locale.Error: unsupported locale setting
>
> Tenho os pacotes language-support-pt e language-pack-pt-base
> insalados. O que mais falta para funcionar?
>>> import locale
>>> print locale.setlocale.__doc__
Set the locale for the given category. The locale can be
a string, a locale tuple (language code, encoding), or None.
Locale tuples are converted to strings the locale aliasing
engine. Locale strings are passed directly to the C lib.
category may be given as one of the LC_* values.
Ou seja, precisa passar o encoding também, como uma string
ou uma tupla:
locale.setlocale(locale.LC_ALL, ('pt_BR', 'UTF8'))
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF8')
HTH,
--
Dorneles Treméa
X3ng Web Technology
http://nosleepforyou.blogspot.com
> Porém no meu Ubuntu 8.04 não estou conseguindo usar
> locale.setlocale(locale.LC_ALL, 'pt_BR') pois levanta o erro de
> 'unsupported locale setting'.
>
> Traceback (most recent call last):
> File "multi_chaves.py", line 15, in <module>
> locale.setlocale(locale.LC_ALL, 'pt_BR')
> File "/usr/lib/python2.5/locale.py", line 478, in setlocale
> return _setlocale(category, locale)
> locale.Error: unsupported locale setting
>
> Tenho os pacotes language-support-pt e language-pack-pt-base
> insalados. O que mais falta para funcionar?
>
Bom, no meu Ubuntu 8.04 eu uso o 'pt_BR.UTF-8'. Ou maneira é colocar uma
string vazia no setlocale(LC_ALL, '') pois assim eu acho que o Python
procura pela encoding padrão do SO.
>
> [ ]s
> Luciano
>
>
--
Pedro Furtado
[As partes desta mensagem que não continham texto foram removidas]
Em Ter, 2008-07-08 às 12:58 -0300, Nilton Volpato escreveu:
> É verdade, você pode usar o locale.strxfrm nesse caso, ao invés do
> locale.strcoll. Veja a documentação do módulo locale. Não é tão
> difícil encontrar isso lá.
>
> Abraço,
> -- Nilton
Muito obrigado, Nilton! Funcionou com strxfrm! Eu também quero agradecer
ao Luciano Ramalho por também ter respondido a minha dúvida.
E aproveitando, o que faz a função a strxfrm (meu inglês é péssimo =D)?
Quando eu executo ela na modo interativo me retornou uns caracteres
esquisitos =)
Obrigado,
Paulo Eduardo Danker
__________________________________________________
Fa�a liga��es para outros computadores com o novo Yahoo! Messenger
http://br.beta.messenger.yahoo.com/
2008/7/8 Luciano Ramalho <ram...@gmail.com>:
> 2008/7/8 Luciano Ramalho <luc...@ramalho.org>:
>> 2008/7/8 Nilton Volpato <nilton....@gmail.com>:
>>> É verdade, você pode usar o locale.strxfrm nesse caso, ao invés do
>>> locale.strcoll. Veja a documentação do módulo locale. Não é tão
>>> difícil encontrar isso lá.
>>
>> Nilton, não entendi para que serve locale.strxfrm. Fiquei com a
>> impressão de que é algum tipo de otimização apenas.
Sim, pode ser visto como uma otimização, mas também pode ser uma
maneira diferente e talvez mais simples de encarar esse mesmo
problema.
A invariante é que:
cmp(strxfrm(s1), strxfrm(s2)) == strcoll(s1, s2)
E como strxfrm devolve uma string, dá um pouco mais de liberdade
quando você quiser ordenar por critérios distintos. Imagine, por
exemplo querer ordenar por um inteiro e depois por uma string
localizada e depois por uma seqüência de bytes quaisquer (string não
localizada).
>> Tentei resolver o problema mencionado pelo Paulo com um exemplo aqui:
>>
>> http://python.pastebin.com/m52a7d25a
>>
>> Porém no meu Ubuntu 8.04 não estou conseguindo usar
>> locale.setlocale(locale.LC_ALL, 'pt_BR') pois levanta o erro de
>> 'unsupported locale setting'.
>>
>> Traceback (most recent call last):
>> File "multi_chaves.py", line 15, in <module>
>> locale.setlocale(locale.LC_ALL, 'pt_BR')
>> File "/usr/lib/python2.5/locale.py", line 478, in setlocale
>> return _setlocale(category, locale)
>> locale.Error: unsupported locale setting
>>
>> Tenho os pacotes language-support-pt e language-pack-pt-base
>> insalados. O que mais falta para funcionar?
Eu acho que você não tem o locale pt_BR gerado. Dá pra listar os
locales disponíveis com o comando: locale -a
O pt_BR geralmente é o com encoding iso-8859-1, acho que é possível
que você só tenha o pt_BR.UTF-8. Para gerar um novo locale no Gentoo
eu tenho que editar o arquivo /etc/locale.gen e rodar o comando
locale-gen, acho que no ubuntu deve ser semelhante.
Mas, usando:
>>> locale.setlocale(locale.LC_ALL, '')
ele pega o locale corrente, geralmente o que estiver definido nas
variáveis de ambiente LC_ALL, LC_* ou LANG.
> Apenas completando, o problema pode ser visto na saída do último laço:
[...]
> Por causa do acento, São Paulo ficou no lugar errado.
[...]
Acho que configurando o locale correto essa ordenação deve funcionar.
No entanto, ordenar por x['uf'] + ' ' + x['nome'] é bem diferente de
ordenar por (x['uf'], x['nome']). Nesse caso funciona porque a UF tem
sempre um tamanho fixo, mas não funcionaria no caso geral. Nesse caso,
usar locale.strxfrm fica mais fácil bastando ordenar pela chave
(strxfrm(x['uf']), strxfrm(x['nome'])). Mas também daria para usar
locale.strcoll usando uma função de comparação personalizada:
def cmp_estado(x, y):
c = locale.strcoll(x['uf'], y['uf'])
if c != 0:
return c
else:
return locale.strcoll(x['nome'], y['nome'])
Dá para economizar e escrever essa mesma função assim:
def cmp_estado(x, y):
return locale.strcoll(x['uf'], y['uf']) or
locale.strcoll(x['nome'], y['nome'])
Exceto que, usando locale.strcoll fica um pouco mais lento do que
locale.strxfrm porque locale.strcoll tem que ser chamado várias vezes,
muitas dessas vezes para valores de chave repetidos, e locale.strxfrm
seria chamado apenas uma única vez por item.
Mas, mesmo embora usar locale.strxfrm seja melhor, se você estiver
usando strings unicode, o work-around é ficar com o strcoll (por
enquanto), por causa desse bug: http://bugs.python.org/issue2481
Abraços,
-- Nilton
Valeu, Dorneles! Sempre é bom ver a documentação. Eu tinha lido neste
caso, mas ela não estava clara, porque ficou para mim a impressão de
que os parâmetris seriam uma string ou uma tupla, e que no primeiro
caso a string seria apenas o language code (do contrário, porque ter
variante da tupla?).
E na própria página do módulo locale [1] tem este exemplo:
>>> locale.setlocale(locale.LC_ALL, 'de_DE') # use German locale; name might vary with platform
[1] http://docs.python.org/lib/module-locale.html
Onde não é especificado nenhum encoding. Foi o que me confundiu.
Atualizei o exemplo lá no pastebin, e agora está 100%:
http://python.pastebin.com/f5735c5dd
Valeo, Deo, a gente sempre pode contar com você para ter dicas
objetivas e precisas!
[ ]s
Luciano
Ótima dica, Pedro. Dos dois jeitos funcionou para mim!
Vamos combinar que é bem esquisita esta API, deve ser vitima dos
pensamento tortuoso de algum programador C. Uma string vazia funciona
para setar o locale, mas se o segundo argumento não for passado ou for
None, então a função aparentemente não faz nada e apenas informa qual
o locale atual? Eu sempre achei que sabia a diferença entre set e
get...
[ ]s
Luciano
Até aqui eu tinha entendido.
> E como strxfrm devolve uma string, dá um pouco mais de liberdade
> quando você quiser ordenar por critérios distintos. Imagine, por
> exemplo querer ordenar por um inteiro e depois por uma string
> localizada e depois por uma seqüência de bytes quaisquer (string não
> localizada).
Legal, valeu o toque, Nilton!
> Eu acho que você não tem o locale pt_BR gerado. Dá pra listar os
> locales disponíveis com o comando: locale -a
>
> O pt_BR geralmente é o com encoding iso-8859-1, acho que é possível
> que você só tenha o pt_BR.UTF-8. Para gerar um novo locale no Gentoo
> eu tenho que editar o arquivo /etc/locale.gen e rodar o comando
> locale-gen, acho que no ubuntu deve ser semelhante.
Na verdade, o problema não era esse, mas a falta de especificar o
encoding na string do locale.
>
> Mas, usando:
>>>> locale.setlocale(locale.LC_ALL, '')
> ele pega o locale corrente, geralmente o que estiver definido nas
> variáveis de ambiente LC_ALL, LC_* ou LANG.
Anotado!
>
>> Apenas completando, o problema pode ser visto na saída do último laço:
> [...]
>> Por causa do acento, São Paulo ficou no lugar errado.
> [...]
>
> Acho que configurando o locale correto essa ordenação deve funcionar.
>
> No entanto, ordenar por x['uf'] + ' ' + x['nome'] é bem diferente de
> ordenar por (x['uf'], x['nome']). Nesse caso funciona porque a UF tem
> sempre um tamanho fixo, mas não funcionaria no caso geral.
Porque não? A intenção é que "ana maria" venha antes de "anaclara
silva", e é isso que acontece.
> Nesse caso,
> usar locale.strxfrm fica mais fácil bastando ordenar pela chave
> (strxfrm(x['uf']), strxfrm(x['nome'])).
Pode ser que hoje eu esteja mais obtuso que de costume, mas não
entendi o que estamos ganhando com o uso do strxfrm neste caso,
Nilton...
O resultado não seria o mesmo se usarmos como chave a tupla abaixo?
(x['uf'], x['nome'])
> Mas também daria para usar
> locale.strcoll usando uma função de comparação personalizada:
>
> def cmp_estado(x, y):
> c = locale.strcoll(x['uf'], y['uf'])
> if c != 0:
> return c
> else:
> return locale.strcoll(x['nome'], y['nome'])
>
> Dá para economizar e escrever essa mesma função assim:
>
> def cmp_estado(x, y):
> return locale.strcoll(x['uf'], y['uf']) or
> locale.strcoll(x['nome'], y['nome'])
>
> Exceto que, usando locale.strcoll fica um pouco mais lento do que
> locale.strxfrm porque locale.strcoll tem que ser chamado várias vezes,
> muitas dessas vezes para valores de chave repetidos, e locale.strxfrm
> seria chamado apenas uma única vez por item.
>
> Mas, mesmo embora usar locale.strxfrm seja melhor, se você estiver
> usando strings unicode, o work-around é ficar com o strcoll (por
> enquanto), por causa desse bug: http://bugs.python.org/issue2481
Grande aula, Nilton! Pena que eu não entendi tudo, mas valeu mesmo assim!
[ ]s
Luciano
2008/7/8 Luciano Ramalho <ram...@gmail.com>:
[...]
>> Eu acho que você não tem o locale pt_BR gerado. Dá pra listar os
>> locales disponíveis com o comando: locale -a
[...]
> Na verdade, o problema não era esse, mas a falta de especificar o
> encoding na string do locale.
Era para funcionar apenas com 'pt_BR' sim, porque o locale 'pt_BR' tem
encoding iso-8859-1. E, diferente do 'pt_BR.UTF-8' a parte do encoding
é implícita. As duas linha abaixo configuram o mesmo locale, mas acho
que nenhuma das duas funcionaria no seu SO (sem gerar esses locales):
>>> locale.setlocale(locale.LC_ALL, 'pt_BR')
'pt_BR'
>>> locale.setlocale(locale.LC_ALL, 'pt_BR.ISO-8859-1')
'pt_BR.ISO-8859-1'
[...]
>> No entanto, ordenar por x['uf'] + ' ' + x['nome'] é bem diferente de
>> ordenar por (x['uf'], x['nome']). Nesse caso funciona porque a UF tem
>> sempre um tamanho fixo, mas não funcionaria no caso geral.
>
> Porque não? A intenção é que "ana maria" venha antes de "anaclara
> silva", e é isso que acontece.
Por exemplo:
>>> ('ana', 'silva') < ('ana clara', 'souza')
True
>>> 'ana'+' '+'silva' < 'ana clara'+' '+'souza'
False
O mais certo é o primeiro, na minha opinião.
>> Nesse caso,
>> usar locale.strxfrm fica mais fácil bastando ordenar pela chave
>> (strxfrm(x['uf']), strxfrm(x['nome'])).
>
> Pode ser que hoje eu esteja mais obtuso que de costume, mas não
> entendi o que estamos ganhando com o uso do strxfrm neste caso,
> Nilton...
>
> O resultado não seria o mesmo se usarmos como chave a tupla abaixo?
>
> (x['uf'], x['nome'])
Se você fosse usar a tupla acima como chave, então teria que usar uma
função de comparação customizada (como aquela que eu passei) para
fazer a ordenação "locale-aware".
Mas, a ordenação leva menos se você passar apenas o argumento key e
não passar cmp (usando o cmp embutido da função sort). Para uma lista
L de tamanho n, o key é chamado exatamente n vezes, já o cmp (que
geralmente já é mais custoso) é chamado O(n log n) vezes. Veja essa
comparação:
>>> def mycmp(x, y):
... mycmp.count += 1
... return cmp(x, y)
...
>>> mycmp.count = 0
>>> def mykey(x):
... mykey.count += 1
... return x
...
>>> mykey.count = 0
>>> import random
>>> L = range(1000000)
>>> random.shuffle(L)
>>> L.sort(cmp=mycmp, key=mykey)
>>> mycmp.count
18605754
>>> mykey.count
1000000
Para cada chamada de key, teve aproximadamente 18 chamadas do cmp para
ordenar essa lista L.
[...]
>> def cmp_estado(x, y):
>> return locale.strcoll(x['uf'], y['uf']) or
>> locale.strcoll(x['nome'], y['nome'])
[...]
> Grande aula, Nilton! Pena que eu não entendi tudo, mas valeu mesmo assim!
Valeu! Se você não entendeu alguma coisa, com certeza foi falta de
didática minha $-)
Abraços,
-- Nilton