Suponha o seguinte:
>>> s = set([1, 2, 3])
Como faço para obter um valor (qualquer valor) s
sem fazer s.pop()
? Quero deixar o item no conjunto até ter certeza de que posso removê-lo - algo que só posso ter certeza após uma chamada assíncrona para outro host.
Rapido e sujo:
>>> elem = s.pop()
>>> s.add(elem)
Mas você sabe de uma maneira melhor? Idealmente em tempo constante.
union
etc., sem tirar elementos dele. Por exemplo,next(iter({3,2,1}))
sempre retorna,1
portanto, se você pensou que isso retornaria um elemento aleatório - não retornaria. Então, talvez você esteja usando a estrutura de dados errada? Qual é o caso de uso?Respostas:
Duas opções que não exigem cópia de todo o conjunto:
Ou...
Mas, em geral, os conjuntos não suportam indexação ou fatia.
fonte
iter(s).next()
não é nojento, mas ótimo. Completamente geral para obter um elemento arbitrário de qualquer objeto iterável. Sua escolha se você quiser ter cuidado se a coleção estiver vazia.next(iter(your_list or []), None)
lidar com nenhum conjunto e conjuntos vaziosO menor código seria:
Obviamente, isso criaria uma nova lista que contém cada membro do conjunto, portanto não é ótimo se o seu conjunto for muito grande.
fonte
next(iter(s))
só ultrapassalist(s)[0]
por três caracteres e é de outro modo dramaticamente superior em ambos tempo e espaço complexidade. Portanto, embora a alegação de "menos código" seja trivialmente verdadeira, também é trivialmente verdade que esta é a pior abordagem possível. Mesmo remover e adicionar manualmente novamente o elemento removido ao conjunto original é superior a "construir um contêiner totalmente novo apenas para extrair o primeiro elemento", o que é claramente insano. O que mais me preocupa é que 38 Stackoverflowers realmente votaram nisso. Só sei que verei isso no código de produção.min(s)
usa ainda menos caracteres e é tão terrível e ineficiente quanto isso.min(s)
é um pouco mais rápido do quenext(iter(s))
para conjuntos de tamanho 1, e cheguei a essa resposta procurando especificamente casos especiais que extraem o único elemento de conjuntos do tamanho 1.Gostaria de saber como as funções serão executadas para conjuntos diferentes, então fiz um benchmark:
Este lote mostra claramente que algumas abordagens (
RandomSample
,SetUnpacking
eListIndex
) dependem do tamanho do conjunto e deve ser evitado no caso geral (pelo menos se o desempenho pode ser importante). Como já mostrado pelas outras respostas, o caminho mais rápido éForLoop
.No entanto, desde que uma das abordagens de tempo constante seja usada, a diferença de desempenho será insignificante.
iteration_utilities
(Isenção de responsabilidade: sou o autor) contém uma função de conveniência para este caso de usofirst
::Eu também o incluí no benchmark acima. Ele pode competir com as outras duas soluções "rápidas", mas a diferença não é grande.
fonte
tl; dr
for first_item in muh_set: break
continua sendo a abordagem ideal no Python 3.x. Maldito seja, Guido.você faz isso
Bem-vindo a mais um conjunto de horários do Python 3.x, extrapolado de wr. é excelente resposta específica de 2.x do Python . Diferentemente da resposta específica do Python 3.x, igualmente útil, do AChampion , os intervalos abaixo também cronometram as soluções mais extremas sugeridas acima - incluindo:
list(s)[0]
, A nova solução baseada em sequência de John .random.sample(s, 1)
, dF. solução eclética baseada em RNG .Trechos de código para grande alegria
Ligue, sintonize, cronometre:
Temporizações atemporais rapidamente obsoletas
Ver! Ordenados pelos snippets mais rápidos e mais lentos:
Faceplants para toda a família
Sem surpresa, a iteração manual permanece pelo menos duas vezes mais rápida que a próxima solução mais rápida. Embora a diferença tenha diminuído nos dias Bad Old Python 2.x (nos quais a iteração manual foi pelo menos quatro vezes mais rápida), decepciona o fanático do PEP 20 em mim, que a solução mais detalhada é a melhor. Pelo menos converter um conjunto em uma lista apenas para extrair o primeiro elemento do conjunto é tão horrível quanto o esperado. Graças a Guido, que sua luz continue a nos guiar.
Surpreendentemente, a solução baseada em RNG é absolutamente horrível. Conversão de lista é ruim, mas
random
realmente leva o bolo de molho horrível. Tanta coisa para o Deus do Número Aleatório .Eu só queria que os amorfos já tivessem um
set.get_first()
método para nós. Se você está lendo isso, eles: "Por favor, faça alguma coisa".fonte
next(iter(s))
é duas vezes mais lento do quefor x in s: break
emCPython
é meio estranho. Eu quero dizer que éCPython
. Será cerca de 50 a 100 vezes (ou algo parecido) mais lento que C ou Haskell fazendo a mesma coisa (na maior parte do tempo, especialmente na iteração, sem eliminação de chamada de cauda e sem otimizações). Perder alguns microssegundos não faz uma diferença real. Você não acha? E também tem oPara fornecer alguns números de tempo por trás das diferentes abordagens, considere o código a seguir. O get () é minha adição personalizada ao setobject.c do Python, sendo apenas um pop () sem remover o elemento.
A saída é:
Isso significa que a solução for / break é a mais rápida (às vezes mais rápida que a solução get () personalizada).
fonte
for x in s
também? "Um iterador é criado para o resultado doexpression_list
."s.remove()
ao misturar ositer
exemplos tantofor
eiter
ir catastroficamente ruim.Como você deseja um elemento aleatório, isso também funcionará:
A documentação não parece mencionar o desempenho de
random.sample
. De um teste empírico muito rápido, com uma lista enorme e um conjunto enorme, parece haver tempo constante para uma lista, mas não para o conjunto. Além disso, a iteração sobre um conjunto não é aleatória; o pedido é indefinido, mas previsível:Se a aleatoriedade for importante e você precisar de vários elementos em tempo constante (conjuntos grandes), eu usaria
random.sample
e converteria em uma lista primeiro:fonte
choice()
, porque o Python tentará indexar seu conjunto e isso não funciona.Aparentemente, a maneira mais compacta (6 símbolos), embora muito lenta , de obter um elemento definido (possibilitado pelo PEP 3132 ):
Com o Python 3.5+, você também pode usar esta expressão de 7 símbolos (graças ao PEP 448 ):
Ambas as opções são aproximadamente 1000 vezes mais lentas na minha máquina do que o método for-loop.
fonte
Eu uso uma função de utilidade que escrevi. Seu nome é um tanto enganador, porque meio que implica que pode ser um item aleatório ou algo assim.
fonte
Seguindo @wr. post, obtenho resultados semelhantes (para Python3.5)
Resultado:
No entanto, ao alterar o conjunto subjacente (por exemplo, chamar para
remove()
), as coisas correm mal nos exemplos iteráveis (for
,iter
):Resulta em:
fonte
O que costumo fazer para coleções pequenas é criar um tipo de método analisador / conversor como este
Então eu posso usar a nova lista e acessar pelo número do índice
Como lista, você terá todos os outros métodos com os quais pode precisar trabalhar.
fonte
list
vez de criar um método conversor?Que tal
s.copy().pop()
? Ainda não cronometrei, mas deve funcionar e é simples. No entanto, funciona melhor para conjuntos pequenos, pois copia todo o conjunto.fonte
Outra opção é usar um dicionário com valores dos quais você não se importa. Por exemplo,
Você pode tratar as chaves como um conjunto, exceto que elas são apenas uma matriz:
Um efeito colateral dessa opção é que seu código será compatível com versões anteriores e mais
set
antigas do Python. Talvez não seja a melhor resposta, mas é outra opção.Edit: Você pode até fazer algo assim para esconder o fato de que você usou um dict em vez de uma matriz ou conjunto:
fonte