Notas básicas do Python para desenvolvimento de testes (29): filas e suas operações

Queue é uma estrutura de dados de tabela linear comum.As filas geralmente são usadas em cenários com recursos limitados, como pools de threads, pools de conexão de banco de dados e assim por diante. Quando não há encadeamentos ociosos no conjunto de encadeamentos, quando uma nova tarefa solicita recursos de encadeamento, as tarefas são enfileiradas e quando há encadeamentos ociosos, as tarefas enfileiradas são retiradas para continuar o processamento.

26.1 Operações Básicas da Fila

Sua característica típica é o primeiro a entrar, o primeiro a sair. Ou seja, aqueles que entram na fila primeiro são desenfileirados e aqueles que entram na fila por último são desenfileirados. As filas têm duas operações básicas: enqueue, que coloca uma parte dos dados no final da fila, e dequeque, que remove uma parte dos dados do início da fila. A figura a seguir é um diagrama esquemático da operação de uma fila unidirecional:
insira a descrição da imagem aqui

26.2 Filas Limitadas e Não Limitadas

As filas podem ser divididas em filas ilimitadas e filas limitadas de acordo com o número de dados contidos nelas. Uma fila ilimitada pode armazenar qualquer quantidade de dados, enquanto uma fila limitada pode armazenar um número limitado de dados. O tamanho da fila deve ser definido de forma razoável. Se a fila for muito grande, haverá muitas tarefas em espera. Se a fila for muito pequena, os recursos do sistema não poderão ser totalmente utilizados.

26.3 Filas fornecidas pelo Python

O Python fornece uma fila de módulo relacionada à fila, que fornece três filas comumente usadas, a saber:

  • Fila do primeiro a entrar, primeiro a sair (FIFO) Fila, fila comum, ou seja, o primeiro inserido é excluído primeiro.
  • Fila de prioridade PriorityQueue, de acordo com a ordem de prioridade para determinar a ordem da fila.
  • Fila Last in, first out (LIFO) LifoQueue, semelhante a uma pilha, os itens adicionados mais recentemente são recuperados primeiro.

Além disso, o módulo de coleções do Python fornece um deque de fila de extremidade dupla, que pode ser enfileirado e desenfileirado em ambas as extremidades da fila, como uma fila ou como uma pilha.

26.3.1 Fila de fila de primeiro a entrar, primeiro a sair (FIFO).

Esta é a fila mais básica em Python. Pressione shift duas vezes no Pycharm, insira queue na caixa de pesquisa, pressione Enter para inserir o arquivo queue.py e clique em Navigate-File Structure no Pycharm. Você pode ver a estrutura de arquivos do módulo de fila. Tanto o LifoQueue quanto o PriorityQueue herdam de Queue. insira a descrição da imagem aqui
A figura acima lista todos os métodos da classe Queue. Vamos dar uma olhada em alguns dos métodos mais usados:

import queue

q = queue.Queue()  # 没有传入maxsize,表示这是个无界队列。
for i in range(5):
    q.put(i) # 放入元素到队列,在队列满时会阻塞

while not q.empty(): # 不为空
    print(q.get(), end=" ")  # 从队列中取元素,在队列为空时会阻塞

Além do enfileiramento e desenfileiramento das operações de bloqueio acima, existem dois métodos de enfileiramento e desenfileiramento sem bloqueio:

  • get_nowait() não bloqueia quando a fila está vazia, ele lançará uma fila de exceção.Empty
  • put_nowait(1) não bloqueia quando a fila está cheia, neste momento uma fila de exceção.Full será lançada
import queue

q = queue.Queue(5)  # 传入maxsize=5,表示这队列长度为5。
for i in range(5):
    try:
        q.put_nowait(i) # 放入元素到队列,在队列满时会阻塞
    except queue.Full as e:
        print("队列满了")
        
while True:
    try:
        print(q.get_nowait())  # 在队列为空的时候也不阻塞,这时候会抛异常queue.Empty
    except queue.Empty:
        print('队列为空')
        break

Essa fila é mais comumente usada no modelo produtor-consumidor. Nesse modelo, os produtores colocam os dados na fila e os consumidores leem os dados da fila. Este modelo realiza o desacoplamento entre sistemas, produtores e consumidores são sistemas independentes que transmitem dados através de filas, podendo ser expandidos e otimizados horizontalmente sem afetar um ao outro.
Aqui está um exemplo de um produtor e consumidor completo (original):

import concurrent.futures
import os
import queue
import random
import threading
import time


# 生产者,定义生产逻辑,将生产的数据放入Queue中
def produce(q):
    production = '{}-{}'.format(threading.current_thread().name, int(time.time()))
    time.sleep(random.randint(1, 2))
    production_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    print('时间:%s, %s 生产了%s, 库存是%d' % (production_date, threading.current_thread().name, production, q.qsize()))
    q.put(production)

# 消费者,定义消费数据的逻辑,从队列中取数据
def consume(q):  
    production = q.get()
    time.sleep(random.randint(1, 2))
    consume_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    print('时间:%s, %s 购买了%s, 库存是%d' % (consume_date, threading.current_thread().name, production, q.qsize()))


if __name__ == '__main__':
    # 生产者线程池,实现多个生产者同时生产
    def produce_pool(workers, q):
        with concurrent.futures.ThreadPoolExecutor(max_workers=workers,
                                                   thread_name_prefix='producer') as executor:
            while True:
                executor.submit(produce, q)

    # 消费者线程池,实现多个消费者同时消费
    def consume_pool(workers, q):
        with concurrent.futures.ThreadPoolExecutor(max_workers=workers,
                                                   thread_name_prefix='consumer') as executor:
            while True:
                executor.submit(consume, q)


    qq = queue.Queue(20)
    produce_workers = os.cpu_count()  # 生产者线程数量
    consume_workers = os.cpu_count()  # 消费者线程数量
    producer_thread = threading.Thread(target=produce_pool, args=(produce_workers, qq))
    consumer_thread = threading.Thread(target=consume_pool, args=(consume_workers, qq))
    producer_thread.start()
    consumer_thread.start()
    producer_thread.join()
    consumer_thread.join()

Neste exemplo, o produtor e o consumidor têm seus próprios conjuntos de encadeamentos. Eles podem melhorar seus respectivos recursos ajustando o número de seus próprios conjuntos de encadeamentos. Por exemplo, se os dados na fila atingirem o valor máximo da fila por um longo tempo, isso significa que o consumidor está lento no consumo.Se o número de threads do consumidor for muito pequeno, você pode expandir o valor de comsume_workers e melhorar a capacidade de consumo. Se for constatado que os dados na fila estão vazios por muito tempo, isso significa que o produtor está cheio ou o número de encadeamentos do produtor é muito pequeno. Você pode expandir o valor de Produce_workder para fornecer capacidade de produção.

O exemplo acima cria threads independentes para o pool de threads do produtor e do pool de threads do consumidor.Após executar o código, você verá vários produtores e consumidores trabalhando ao mesmo tempo.

时间:2020-06-07 12:26:28, producer_0 生产了producer_0-1591503986, 库存是2
时间:2020-06-07 12:26:28, producer_3 生产了producer_3-1591503986, 库存是3
时间:2020-06-07 12:26:28, consumer_1 购买了producer_2-1591503984, 库存是4
时间:2020-06-07 12:26:28, consumer_3 购买了producer_5-1591503984, 库存是2
时间:2020-06-07 12:26:29, consumer_7 购买了producer_0-1591503984, 库存是1

O produtor-consumidor no exemplo acima apenas imprime algum conteúdo no Console. No trabalho real, os consumidores geralmente retornam os dados processados ​​ao chamador. Vamos transformar, se o consumidor tem um valor de retorno, como lidar com isso.

Primeiro, a função comsume adiciona uma instrução return que retorna a avaliação da produção de mercadorias. Então, na função de pool de threads consumidor comsume_pool, o resultado da execução da função de consumo é obtido através do método result da instância futura.

def consume(q):  # 消费者改进版,对消费的产品给出评价
    production = q.get()
    return "{} 很棒~,好评".format(production)
    

def consume_pool(workers, q):
    with concurrent.futures.ThreadPoolExecutor(max_workers=workers,
                                               thread_name_prefix='consumer') as executor:
        to_do = []  # 存放future实例
        results = []  # 存储future实例的结果
        while True:
            to_do.append(executor.submit(consume, q))  # submit返回创建好的 future 实例,放入to_do列表
            if len(to_do) >= 5:
                for done_future in concurrent.futures.as_completed(to_do):  # 返回任务的对象,这里不耗时
                    results.append(done_future.result())  # 耗时,注意取得的结果是乱序的
                print(results)
                to_do = []

26.3.2 Fila da fila Last In, First Out (LIFO).LifoQueue

Esta é uma estrutura de dados semelhante a uma pilha, e os dados que chegam na última fila saem primeiro. Por meio de Navigate - Type Hierachy do Pycharm, você pode ver que o LifoQueue herda de Queue.
insira a descrição da imagem aqui
Ao olhar para o código fonte do LifoQueue, verifica-se que não existe um método especial em si. Sua operação ainda está usando o método de sua classe pai Queue, mas para o método get, é definida aqui sua própria lógica, que é obter dados do final da fila.

class LifoQueue(Queue):
    '''Variant of Queue that retrieves most recently added entries first.'''

    def _init(self, maxsize):
        self.queue = []

    def _qsize(self):
        return len(self.queue)

    def _put(self, item):
        self.queue.append(item)

    def _get(self):
        return self.queue.pop()  # 特殊点,从队尾删除

A partir do código-fonte de Queue, você pode ver que o método get obtém dados do início da fila:

# Get an item from the queue
    def _get(self):
        return self.queue.popleft()

Esta é toda a diferença entre LifoQueue e Queue. Por exemplo, sinta:

import queue

q = queue.LifoQueue()
for i in range(5):
    q.put(i)

while not q.empty():
    print(q.get(), end=" ")

26.3.3 Fila de fila de prioridade.PriorityQueue

PrirotiyQueue também é herdado da classe Queue. Ao buscar dados dela, eles são buscados por prioridade. Ao colocar dados, uma prioridade deve ser atribuída aos dados. A prioridade é um número, quanto menor o número, maior a prioridade e mais preferencial deve ser retirado.

class PriorityQueue(Queue):
    '''Variant of Queue that retrieves open entries in priority order (lowest first).

    Entries are typically tuples of the form:  (priority number, data).
    '''

    def _init(self, maxsize):
        self.queue = []

    def _qsize(self):
        return len(self.queue)

    def _put(self, item):
        heappush(self.queue, item)

    def _get(self):
        return heappop(self.queue)

Pode-se ver que o método get de dados e o método put de dados são implementados pelo módulo heap heapq em Python. Ao colocar dados no método put, o formato é uma tupla, o primeiro elemento é o número de prioridade e o segundo elemento são os dados.

Dê um exemplo para ver:

from queue import PriorityQueue

pq = PriorityQueue()

student_1 = {
    
    'name': 'lisi', 'age': 30}
student_2 = {
    
    'name': 'wangwu', 'age': 29}

pq.put((20, student_1))  # 第一个参数指定一个优先级
pq.put((9, student_2))

# 越小的数字代表越高的优先级
while not pq.empty():
    print(pq.get())  # 按照优先级取  ,先取出student_2再取出student_1

26.3.4 Deque coleções.deque

Deque é a abreviação de double-ended queue.Deque pode operar em ambas as extremidades, então deque suporta métodos de operação avançados.

insira a descrição da imagem aqui

Digite o seguinte código no Pycharm para visualizar todos os métodos da classe deque.

import collections

print(dir(collections.deque))
# ['__add__', '__bool__', '__class__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'appendleft', 'clear', 'copy', 'count', 'extend', 'extendleft', 'index', 'insert', 'maxlen', 'pop', 'popleft', 'remove', 'reverse', 'rotate']

Mantenha pressionada a tecla de comando e clique com o botão esquerdo do mouse no deque. Você pode inserir o arquivo de origem da classe deque. Há instruções para usar cada método no código-fonte. Aqui estão alguns exemplos de métodos comumente usados:

import collections

d1 = collections.deque(maxlen=5)
# 右边操作
d1.extend('abcdefg')
print('extend right:', d1)
d1.append('h')
print('append right:', d1)
d1.rotate(2)
print('Rotate the deque 2 steps to the right ', d1) 
print(d1.pop())
print(d1)
# 左边操作
d2 = collections.deque()
d2.extendleft(range(6))
print('extend left:', d2)
d2.appendleft(6)
print('append left:', d2)
print(d2.popleft())
print(d2)
# 像列表一样操作
print(d2[0], d2[-1])
d2.remove(3)
print(d2)
print(d2.count(0))

O deque é thread-safe, o que significa que as operações dos lados esquerdo e direito do deque não afetarão umas às outras ao mesmo tempo. Veja o código a seguir:

import collections
import threading
import time

candle = collections.deque(maxlen=20)
candle.extend("abcdefghijklmn")


def burn(direction, nextSource):
    while True:
        try:
            next = nextSource()
        except IndexError:
            break
        else:
            print('%s : %s' % (direction, next))
            time.sleep(0.1)
    print("done %s" % direction)
    return


left = threading.Thread(target=burn, args=('left', candle.popleft))
right = threading.Thread(target=burn, args=('right', candle.pop))

left.start()
right.start()

left.join()
right.join()

O parâmetro maxlen em deque é muito útil. Use maxlen em deque para criar uma fila de tamanho fixo e adicione elementos a ela por meio de append. Quando o número de elementos atinge maxlen, o primeiro elemento adicionado a deque é removido automaticamente e novos elementos são adicionados. Assim, sempre mantendo os elementos maxlen mais recentes no deque.

Vejamos um exemplo, em uma lista de strings, procure uma string correspondente e, quando a primeira pesquisa for encontrada, imprima as n strings atuais e anteriores. Nesse cenário, precisamos salvar um número limitado de elementos. Vejamos o código abaixo:

from collections import deque


def search(lst, pattern, history):  # 当编写搜索某一项记录的代码时,通常会用到含有yield关键字的生成器。这将处理搜索过程的代码和使用搜索结果的代码解耦
    previous_elements = deque(maxlen=history)
    for l in lst:
        if pattern in l:
            yield l, previous_elements
            break
        previous_elements.append(l)


if __name__ == '__main__':
    persons = ['I', 'am', 'learning', 'python', 'by', 'coding']

    for item, records in search(persons, 'b', 2):
        for r in records:
            print(r, end=' ')
        print(item)

deque(maxlen=history)Uma fila de comprimento fixo de histórico de comprimento é criada. Quando um novo elemento é adicionado à fila, mas a fila está cheia neste momento, o registro mais antigo é removido automaticamente.

26.4 Perguntas da entrevista

  1. Corresponde a cada linha no arquivo, quando combinada, gera a linha correspondente e as N linhas que acabaram de ser correspondidas
from collections import deque


def search(text, pattern, history):
    previous_lines = deque(maxlen=history)
    for line in text:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)


if __name__ == '__main__':
    with open("java.txt") as f:
        for line, previous in search(f, 'javascript', 2):
            for p in previous:
                print(p, end='')
            print(line)

Dado um array não vazio de inteiros, retorne os k elementos mais frequentes nele.

Exemplo 1:

Entrada: nums = [1,1,1,2,2,3], k = 2
Saída: [1,2]
Exemplo 2:

Entrada: nums = [1], k = 1
Saída: [1]
Explicação:

Você pode assumir que um dado k é sempre razoável e que 1 ≤ k ≤ o número de elementos distintos na matriz.

A complexidade de tempo do seu algoritmo deve ser melhor que O(n log n) , onde n é o tamanho do array.

class Solution:
    def topKFrequent(self, nums, k: int):
        if k<0 or k>len(nums): return []
        from queue import PriorityQueue
        from collections import defaultdict
        queue = PriorityQueue()  # 优先级队列
        d = defaultdict(int)  # 值是int的字典
        res = []
        for i in nums:
            d[i]+=1
        d = list(d.items())
        print(d)
        for i in range(len(d)):
            queue.put([-d[i][1],d[i][0]])  # 这里第一个参数要用,要用负数,优先级队列中越小优先级越高
        for i in range(k):
            res.append(queue.get()[1])
        return res

Acho que você gosta

Origin blog.csdn.net/liuchunming033/article/details/106029395
Recomendado
Clasificación