Código como esse geralmente acontece:
l = []
while foo:
#baz
l.append(bar)
#qux
Isso é muito lento se você estiver prestes a anexar milhares de elementos à sua lista, pois a lista precisará ser constantemente redimensionada para se ajustar aos novos elementos.
Em Java, você pode criar um ArrayList com uma capacidade inicial. Se você tem alguma idéia de quão grande será sua lista, isso será muito mais eficiente.
Entendo que códigos como esse geralmente podem ser redefinidos em uma compreensão de lista. Se o loop for / while for muito complicado, isso é inviável. Existe algum equivalente para nós programadores Python?
python
list
dictionary
initialization
Claudiu
fonte
fonte
Respostas:
Resultados . (avalie cada função 144 vezes e calcule a média da duração)
Conclusão . Isso quase não importa.
Otimização prematura é a raiz de todo o mal.
fonte
As listas Python não têm pré-alocação embutida. Se você realmente precisa fazer uma lista e evitar a sobrecarga de anexar (e deve verificar se o faz), faça o seguinte:
Talvez você possa evitar a lista usando um gerador:
Dessa forma, a lista nem todos são armazenados na memória, apenas gerados conforme necessário.
fonte
Versão curta: use
pré-alocar uma lista (ou seja, poder endereçar elementos de 'tamanho' da lista em vez de gradualmente formar a lista anexando). Esta operação é MUITO rápida, mesmo em grandes listas. A alocação de novos objetos que serão atribuídos posteriormente aos elementos da lista levará MUITO mais tempo e será o gargalo no seu programa, em termos de desempenho.
Versão longa:
Eu acho que o tempo de inicialização deve ser levado em consideração. Como no python tudo é uma referência, não importa se você define cada elemento como None ou alguma string - de qualquer forma, é apenas uma referência. Embora demore mais tempo, se você deseja criar um novo objeto para cada elemento a ser referenciado.
Para Python 3.2:
Avaliação:
Como você pode ver, apenas fazer uma grande lista de referências ao mesmo objeto None leva muito pouco tempo.
Anexar ou estender leva mais tempo (não calculei a média de nada, mas depois de executar isso algumas vezes, posso dizer que estender e anexar levam aproximadamente o mesmo tempo).
Alocar novo objeto para cada elemento - é isso que leva mais tempo. E a resposta de S.Lott faz isso - formata uma nova string sempre. O que não é estritamente necessário - se você quiser pré-alocar algum espaço, faça uma lista de Nenhum e atribua dados aos elementos da lista à vontade. De qualquer maneira, leva mais tempo para gerar dados do que anexar / estender uma lista, seja você gerada durante a criação da lista ou depois disso. Mas se você deseja uma lista pouco povoada, começar com uma lista de Nenhum é definitivamente mais rápido.
fonte
[]*
abordagemO caminho pitônico para isso é:
ou qualquer valor padrão com o qual você deseja pré-pop-up, por exemplo
[EDIT: Caveat Emptor A
[Beer()] * 99
sintaxe cria umaBeer
e preenche uma matriz com 99 referências à mesma instância única]A abordagem padrão do Python pode ser bastante eficiente, embora essa eficiência decaia à medida que você aumenta o número de elementos.
Comparar
com
No meu Windows 7 i7, o Python de 64 bits oferece
Enquanto o C ++ fornece (construído com MSVC, 64 bits, otimizações ativadas)
A compilação de depuração do C ++ produz:
O ponto aqui é que, com o Python, você pode obter uma melhoria de desempenho de 7 a 8%; se você acha que está escrevendo um aplicativo de alto desempenho (ou se está escrevendo algo que é usado em um serviço da Web ou algo assim), então isso não deve ser desprezado, mas pode ser necessário repensar sua escolha de idioma.
Além disso, o código Python aqui não é realmente um código Python. Mudar para o código verdadeiramente Pythonesque aqui oferece melhor desempenho:
Que dá
(no doGenerator de 32 bits é melhor que doAllAllate).
Aqui, a diferença entre doAppend e doAllocate é significativamente maior.
Obviamente, as diferenças aqui realmente se aplicam apenas se você estiver fazendo isso mais do que algumas vezes ou se estiver fazendo isso em um sistema muito carregado, em que esses números serão redimensionados por ordens de magnitude ou se você estiver lidando com listas consideravelmente maiores.
O ponto aqui: faça da maneira pitônica o melhor desempenho.
Mas se você está preocupado com o desempenho geral de alto nível, o Python é a linguagem errada. O problema mais fundamental é que as chamadas de função do Python tradicionalmente são até 300 vezes mais lentas que outras linguagens devido a recursos do Python, como decoradores etc. ( https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation ).
fonte
timeit
timeit
, que você deve usar quando cronometrar seu código Python; Não estou falando de C ++, obviamente.bottles = [Beer()] * 99
não cria 99 objetos Beer. Em vez disso, cria um objeto Beer com 99 referências a ele. Se você o alterar, todos os elementos da lista serão alterados, causa(bottles[i] is bootles[j]) == True
para todosi != j. 0<= i, j <= 99
.Como outros mencionaram, a maneira mais simples de pré-propagar uma lista com
NoneType
objetos.Dito isto, você deve entender como as listas Python realmente funcionam antes de decidir que isso é necessário. Na implementação de uma lista do CPython, a matriz subjacente é sempre criada com espaço para sobrecarga, em tamanhos progressivamente maiores
( 4, 8, 16, 25, 35, 46, 58, 72, 88, 106, 126, 148, 173, 201, 233, 269, 309, 354, 405, 462, 526, 598, 679, 771, 874, 990, 1120, etc)
, para que o redimensionamento da lista não ocorra com tanta frequência.Devido a esse comportamento, a maioria das
list.append()
funções é deO(1)
complexidade para anexos, tendo apenas uma complexidade aumentada ao cruzar um desses limites, momento em que a complexidade seráO(n)
. Esse comportamento é o que leva ao aumento mínimo no tempo de execução na resposta de S. Lott.Fonte: http://www.laurentluce.com/posts/python-list-implementation/
fonte
Corri o código do @ s.lott e produzi o mesmo aumento de 10% no perf pré-alocando. tentou a idéia de @ jeremy usando um gerador e foi capaz de ver o desempenho do gen melhor do que o do doAllocate. Para o meu projeto, a melhoria de 10% é importante, portanto, obrigado a todos, pois isso ajuda bastante.
fonte
As preocupações com a pré-alocação no Python surgem se você estiver trabalhando com o numpy, que possui mais matrizes do tipo C. Nesse caso, as preocupações de pré-alocação são sobre a forma dos dados e o valor padrão.
Considere entorpecido se estiver fazendo cálculos numéricos em listas massivas e desejar desempenho.
fonte
Para alguns aplicativos, um dicionário pode ser o que você está procurando. Por exemplo, no método find_totient, achei mais conveniente usar um dicionário, pois não tinha um índice zero.
Esse problema também pode ser resolvido com uma lista pré-alocada:
Sinto que isso não é tão elegante e propenso a erros, porque estou armazenando o None, o que poderia gerar uma exceção se eu acidentalmente os usar incorretamente e porque preciso pensar em casos extremos que o mapa me permita evitar.
É verdade que o dicionário não será tão eficiente, mas como outros comentaram, pequenas diferenças de velocidade nem sempre valem riscos significativos de manutenção.
fonte
Pelo que entendi, as listas python já são bastante semelhantes às ArrayLists. Mas se você quiser ajustar esses parâmetros, achei este post na rede que pode ser interessante (basicamente, basta criar sua própria
ScalableList
extensão):http://mail.python.org/pipermail/python-list/2000-May/035082.html
fonte