Transcrições
1. Apresentação: Olá, desculpe, sou bem-vindo
a este curso que ensina programação Python intermediária a
avançada. Este é um curso com curadoria. Não é uma lista completa de todos os recursos
avançados possíveis que estão disponíveis no Python. Ele contém as coisas que eu acho que
serão mais úteis para você se você quiser dar
o próximo passo em sua carreira de desenvolvimento de
software. Este curso é para você, se você tem experiência em Python, mas gostaria de
aprender um pouco mais sobre os recursos avançados
que a linguagem oferece. Então, vou falar
sobre coisas como dicas
de tipo, código simultâneo , classes de
protocolo,
iteradores, geradores e algumas outras
coisas. Você pode encontrar todos
os exemplos de código usados no curso, na guia Projetos
e Recursos. E também há um
pequeno projeto divertido para você mergulhar no final.
Se você gosta deste curso, talvez também goste do meu
canal no YouTube ou dos códigos de barra de ferro do
youtube.com para jovens potros . Lá eu falei principalmente
sobre design de software em
vários modos de conteúdo em
Python. Então dê uma olhada. Espero que você tenha gostado do curso
, pois está pronto. Vamos mergulhar.
2. Anotações de tipo 1/3: Vou começar
este curso falando sobre sistemas de tipos. Muitas linguagens de programação
têm tipos embutidos em sua sintaxe e há maneiras
diferentes de
ver os sistemas de tipos. E eu quero falar sobre
alguns deles hoje e como isso se relaciona com a forma como os
tipos são tratados em Python. Portanto, a primeira é que temos sistemas do
tipo
estático versus dinâmico. E essa distinção é sobre quando o tipo de
informação é adquirido, o que aconteceria estaticamente? Isso significa que isso
acontece em tempo de compilação ou interpretação
quando é dinâmico, isso significa que os tipos
são determinados em tempo de execução. Outra distinção da qual você já deve
ter ouvido falar é a digitação forte versus a fraca. E isso é mais sobre o quão rigoroso é amarrar de
forma consistente. Então, por exemplo ,
ele converte automaticamente números inteiros em strings ou
vice-versa? Não há uma definição clara
do que exatamente é um sistema de digitação forte
versus um sistema de digitação fraco. É por isso que geralmente
procuramos outras maneiras de
distinguir sistemas de tipos sobre as quais
falarei em um minuto. Cada linguagem de programação
aborda os tipos de forma diferente. Por exemplo, Java é tipado estaticamente. Isso significa que,
se você declarar uma variável do tipo
string e uma string, e então tentar
atribuir um número a ela. Isso não é permitido. E a razão pela qual isso não
é permitido é porque definimos em tempo de
compilação que a variável será do tipo string, então não podemos mais mudar isso. O Python, por outro lado,
é um duto dinâmico, então não há problema em
definir uma variável e
atribuir a ela um valor de string. E, posteriormente, atribuímos ele um valor de um tipo diferente. Basicamente, o tipo não está
associado à variável
, está
associado ao valor i. programa Python
muito simples aqui. Então, se eu definir uma variável
x e atribuir uma string, e então porque o Python
é digitado dinamicamente, posso simplesmente atribuir a ela um
valor de tipos diferentes, neste caso um número inteiro, e tudo bem. Não tem problema algum. Em Java, isso, é
claro, resultaria em erro. Agora, embora o Python
seja digitado dinamicamente, ele também é bastante rígido. Na verdade, você poderia
chamá-lo de fortemente digitado se tivéssemos uma definição
para isso. Então, por exemplo o que eu posso fazer é se
tivermos a mesma variável X, podemos fazer olá mais cinco. Porque se eu tentar executar isso, você
verá que receberá um erro de que, ei, só
podemos concatenar
cadeias de caracteres e não números inteiros. Isso significa que o Python
não
converte automaticamente esse número
cinco em uma string cinco. Claro, eu posso fazer
isso explicitamente. Então, quando eu executo o programa novamente, agora você vê que isso
não vai resultar em
problemas porque, ao desfazer
a conversão de tipo sozinho, isso é diferente de uma
linguagem como JavaScript, que permite que você
faça olá mais cinco. E então eu simplesmente
vou
converter automaticamente o número
em uma string. E também faz sentido do ponto
de vista do JavaScript,
porque o JavaScript é, obviamente, a
linguagem da web. E queremos
garantir que o cliente sempre mostre pelo menos
algo razoável. Portanto, o JavaScript faz o possível para fazer o que pode com
os valores que obtém. Nem sempre funciona, mas isso significa que, mesmo que
uma página da web tenha alguns erros, ela ainda pode mostrar
algo significativo. Quais são algumas outras maneiras de
analisar os sistemas de tipos enquanto um é manifesto versus inferido manifesto significa que você precisa especificar
explicitamente o
tipo de variável, por exemplo C ou Java tem tipagem de manifesto, precisamos especificar
qual é o tipo. Inferido. Digitar significa
que o tipo da variável será
inferido do valor. E é exatamente isso que está
acontecendo em Python. Portanto, em Python, não precisamos
indicar que, nesse caso, x é uma string e
não outra coisa. No entanto, outra forma de
ver os sistemas de tipos é nominal versus estrutural. Nominal significa que,
para comparar se os tipos correspondem, o sistema de tipos está
examinando completamente os nomes. Então, se temos coisas que são cadeias de caracteres,
ambas são do mesmo tipo. Se tivermos duas
coisas
da mesma classe ou classes,
digite também, então os tipos correspondem. Isso é nominal. Estrutural significa que
vamos examinar
a estrutura dos objetos para comparar
se esses tipos coincidem. Então, por exemplo, se temos um objeto
que tem uma propriedade x, então os tipos coincidem
com outro objeto se o objeto também tiver
a mesma propriedade x. Então, realmente observando
a estrutura do objeto versus o
nome do objeto. Então, nesse caso, o
que o Python tem? Bem, não é realmente uma tipagem
estrutural, mas é um pouco semelhante. É algo
chamado digitação de pato. Digitar com pato é como uma
estrutura reforçada, mas não é exatamente a mesma coisa. tipagem estrutural é um sistema de tipo
estático que compara as estruturas dos objetos quando você
compila o código. Doctype é dinâmico, mas só analisa as
partes que importam. Portanto, temos uma certa parte
do objeto que você está
acessando em tempo de execução. Digamos que você tenha
um objeto com uma propriedade x e uma propriedade por quê? E em tempo de execução, você só está acessando a propriedade
X do que os tipos correspondem se o objeto tiver essa propriedade e não
examinarmos a propriedade. Por que, mesmo que possa
ser parte do tipo estático, digitar pato é, na verdade,
o que o Python está usando. Então, ele simplesmente
analisa as peças que são
necessárias durante o tempo de execução. Combine. Python está pronto para usar. Eu tenho um exemplo aqui que
mostra como a digitação de pato funciona. Então, eu tenho algumas
variáveis aqui, minha string ou lista,
meu dicionário. E embora cada um
tenha alguns valores, você vê que temos um comprimento de função que pode imprimir o
comprimento dessas coisas. Portanto, esta não é uma nota 11
porque tem 11 caracteres. Isso se chama quatro porque
há quatro elementos na lista e isso
imprimirá três. E a forma como
isso funciona é que a função de comprimento
espera um objeto. Você já pode vê-lo
aqui do tamanho do tipo. E isso significa que esse
objeto precisa ter métodos
de Lynn Dunder
associados a ele. E esse é o caso das sequências de caracteres,
das listas, dos dicionários. Então, se eu executar isso,
você verá que não há problema em executar esse código e
simplesmente
imprimirá o que eu
queria imprimir. Então, o que acontece é que
dicionários, listas e cadeias de caracteres. Claro que são
tipos de objetos muito diferentes, certo? Mas a digitação de pato
os aceita como parte
dos argumentos da função length porque a única coisa que
é necessária aqui ou que é acessada são os métodos length
dunder. Portanto, desde que essa seja a digitação do pato
deles e o Python esteja
pronto e aceite isso, mesmo que esse tipo não corresponda
estruturalmente exatamente porque uma string não é uma lista e a lista
não é o dicionário. Mas em um sistema de digitação de pato
funciona sem problemas. Você pode até mesmo dar esse
passo adiante, por exemplo ,
aqui, se o livro de aula
que não tem inicializadores é um autor,
título e páginas. E eu mesmo defini aqui o método de
tamanho, e isso só retorna
o número de páginas. E então eu imprimo chamando a função de comprimento
em um objeto de livro. Isso é o que você está vendo aqui. E aqui você
vê que quando eu executo isso, você obtém isso como resultado. Então, neste caso,
funciona exatamente da mesma forma. A única coisa que
a função de comprimento precisa é de um objeto do tamanho do tipo. E isso significa que
deveria ter um método de doação enxuta, que
essa classe representa pela metade. Então, esse código é executado sem problemas, porque é
assim que a digitação com pato funciona.
3. Anotações de tipo 2/3: Com o Python, você
não precisa especificar explicitamente nenhum tipo
ao escrever uma meta. entanto, é uma boa ideia
fazer isso, porque isso ajudará você a tornar seu código mais fácil de ler. E, na
verdade, é muito fácil fazer isso. Por exemplo, aqui temos uma
string que é HelloWorld, mas você pode realmente indicar
usando dicas de tipo ou um tipo de notação qual é
o tipo dessa variável
específica. E você simplesmente faz isso
escrevendo uma coluna e, em seguida, digita o tipo, você digita o tipo aqui. Nós podemos fazer a mesma coisa. Portanto, esta é uma lista de números inteiros. Então, vou escrever
uma lista de números inteiros. E se você tiver um tipo
genérico como esse, poderá usar
os colchetes
para digitar com o botão direito do mouse atrás dele. E para o dicionário,
isso é muito parecido. Então, temos um ditado e o primeiro, o tipo de chave é uma string, certo? Temos 12,3, essas são cadeias de caracteres e o que isso aponta para o
valor é um número inteiro. , aqui temos o tipo de
dicionário propósito, aqui temos o tipo de
dicionário para
usar esse tipo de tipo. E eu vou dizer que você
precisa do Python 3.9 ou mais recente. Caso contrário, você
terá que
escrever digitando import, dict com uma letra maiúscula
e, em seguida, poderá usar isso. Mas se você usa uma
versão recente do Python
, pode simplesmente escrever
isso. É muito mais rápido. Então, é assim que você
especifica os tipos. É claro que a aula também é um tipo. Eu posso escrever algo assim, ser um subtipo de livro. E isso vai ser um livro. E então podemos passar alguns valores para
ele. Algo parecido com isso. Então, é assim que você adiciona dicas de
tipo ao
declarar variáveis. Agora,
isso não é muito útil porque quando você
olha para a linha de código, você já pode ver
que é um livro. Então, por que deveríamos
escrever isso, certo? Não é realmente necessário, mas isso é realmente
útil se você tiver funções ou métodos e quiser especificar dicas
de tipo para argumentos. Isso é realmente o que
você está vendo aqui. Então, vemos que temos um
autor do tipo string. Temos um título do
tipo string e páginas do tipo inteiro. Isso é muito útil
porque, quando chamamos o inicializador do livro aqui, você pode ver que estou usando o código
VS nesse caso. Então, eu já estou recebendo as informações de tipo
no cabeçalho. Agora eu sei
exatamente o que preciso oferecer. Esse autor deveria
ser uma string e vital também deveria
ser uma string. E páginas e um interior. Você verá isso como
um tipo de devolução aqui. Portanto, o inicializador do livro não retorna nada,
e isso está correto. Não há
declaração de devolução aqui. Na verdade, você também pode escrever isso explicitamente, se
quiser, assim. Portanto, ele retorna nenhum e comprimento. Se você observar isso,
ele retornará um número inteiro. Portanto, também podemos especificar
isso explicitamente. Uma coisa que você pode se
perguntar é o que acontece se especificarmos o
tipo errado, por exemplo, aqui temos uma variável
do tipo string, certo? E atribuímos a string,
então está tudo correto. Mas o que acontece se eu transformar
isso em um número inteiro, por exemplo, então isso claramente não é
um número inteiro, certo? Então, se executarmos isso, você verá que o código realmente é executado
sem problemas, mesmo que o tipo
esteja completamente errado. E é por isso que o Python
não analisa esses tipos quando está
realmente executando o programa. Na verdade, eles estão
lá apenas para nos ajudar, como desenvolvedores, a entender
onde
estão os problemas em nosso programa Você vê que nem mesmo recebemos
um erro aqui e é
aí que as configurações do VS Code
podem nos ajudar a especificar o quão rigoroso
queremos que nosso sistema de
verificação de tipo em
nosso IDE seja aberto. Agora, nas configurações
do espaço de trabalho no VS Code e
nos produtos lácteos, há um molde de
verificação do tipo de ponto de análise de pontos em Python que está desativado. Então, o que você pode fazer é alterar esse valor para
determinar o quão rigorosa deve ser a verificação
de tipos. E há basicamente
três opções. Existe o Off básico e o mais rigoroso. Então, deixe-me mudar isso para estrito. Assim. Agora vamos voltar ao meu
arquivo e ousar ver, ei, agora estamos
tendo um problema. Veja, minha string
é um número inteiro, mas estamos atribuindo a ela uma string. E você também vê que recebemos um erro
de incompatibilidade fornecido pelos pilotos, que é a superfície da
linguagem Python do VSCode. Então, deixe-me mudar isso de
volta para uma string ou você, aliás, você
também a verá aqui. Essa é a minha string é um número inteiro. Vejo que aqui
também temos um problema que int pode ser
atribuído porque
não está fora do tamanho de um tipo e não tem um método de comprimento dunder. Então, vamos mudar isso de volta para string e então veremos
se o problema foi resolvido.
4. Digitação de pato com cursos de protocolo: Um tipo bastante específico de
Python o qual eu quero
falar com você, e essa é a classe de protocolo. Então, vimos
no outro exemplo que
podemos ter classe. E a aula é,
obviamente, um tipo. Mas o que acontece com frequência
é que você está usando classes e objetos de um tipo específico
em todo lugar do seu código, certo? Então, aqui eu tenho um exemplo
disso em que temos uma classe chamada
clientes de e-mail ou
digamos que este seja um sistema de
envio de e-mail. E você vê que o que está acontecendo aqui é que temos
o inicializador que obtém um monte de
informações, como logins ,
senhas, etc. Mas dentro do inicializador, criamos um servidor SMTP. Em seguida, tentamos obter o
host e a porta do objeto
do servidor SMTP
que acabamos de criar. E então estamos fazendo
algumas coisas, como verificar o nome de usuário e a senha e armazenar esses dois endereços
e assim por diante. E então temos
alguns métodos como conectar-se ao servidor, sair do servidor
e enviar uma mensagem, enviar um e-mail por meio desse
surfista com algumas coisas úteis como
adicionar sistematicamente os dois endereços e coisas assim. Portanto, temos muitos tipos aqui. Você vê que temos aqui
os tipos de devolução
que, em sua maioria, não são. Também estamos usando outros tipos de anotações de tipo
aqui, por exemplo,
isso significa que o login pode ser
uma string ou pode ser conhecido. Portanto, isso também é chamado de sintaxe
do tipo de união. Isso significa que você pode combinar
vários tipos e, para um padrão do tipo or,
atribuímos a ele o valor none. Você também vê que confiamos
aqui no tipo SMTP, que vem da
biblioteca SMTP, certo? E isso não é um
problema por si
só, mas significa que
não há dependência entre seus clientes de e-mail e uma classe SMTP
muito específica. Se você quisesse usar
uma biblioteca para isso, teria que entrar na implementação
da classe e
corrigir as referências ao SMTP
e, em seguida, remover essa importação. Outra forma de fazer isso
é usando protocolos. E com o protocolo,
o que você faz é uma extensão muito boa
do mecanismo de digitação de pato. Em Python com protocolo, você especifica qual será a estrutura do objeto. Então essa é a parte estrutural da digitação de pato e o que você espera. E então o sistema de digitação de
patos do Python vai igualar
isso em tempo de execução. Então, eu tenho outro
exemplo aqui em eu usei esse mecanismo. E essa é
exatamente a mesma classe. Então, temos novamente aqui
o cliente de e-mail, mas agora você vê que há
outra classe escrita acima dele
chamada Onde estamos? Servidor de e-mail? Um servidor de e-mail é
uma classe de protocolo. Você vê que
importamos isso da digitação. E então há algumas propriedades e métodos
aqui que eu defini. Então, há o host e
as setas para conectar os métodos. Portanto, esses são os
métodos padrão que
também estão na essência
da classe de servidor B. Mas agora o que fazemos é passar para os clientes de e-mail uma
instância desse servidor SMTP
e, em seguida, simplesmente usá-la. E o que é bom é
que, como agora simplesmente fornecemos o
protocolo como um tipo, podemos usar o servidor SMTP da
mesma forma que fizemos antes. Simplesmente precisamos criá-lo
fora do cliente de e-mail. propósito, isso se chama
injeção de dependência. E se quisermos
substituí-lo por outra coisa, por exemplo, queremos usar
outro servidor SMTP ou talvez queiramos usar um servidor SMTP falso para que
possamos escrever alguns
testes de software para essa classe. Bem, então podemos
simplesmente fornecer isso. E desde que a
classe que fornecemos, esse objeto que fornecerá
adira a esse protocolo. Portanto, desde que
tenha esses métodos e o doctype corresponda
a eles
, podemos usá-lo em
combinação com o cliente de e-mail. E também perdemos a importação muito
específica aqui da biblioteca SMTP porque agora estamos simplesmente confiando
em um protocolo. Portanto, o protocolo permite que você
introduza alguma abstração em seu código, para que você não precise se preocupar muito em ter
muitos e muitos
acoplamentos, muitas e muitas
dependências
diretas em bibliotecas de baixo nível, calças
justas, como acabei de mostrar algo muito útil para aumentar
a legibilidade do
seu código, que pode ser
útil se você
trabalhar
em a legibilidade do
seu código, que pode ser equipe nesse código ou se você escrever o código e,
alguns meses depois voltar a ele e tentar descobrir o que está acontecendo e o que você deve passar
como argumento para uma
função ou algo assim. Dicas de digitação são muito
úteis para esclarecer isso e garantir que você
cometa menos erros como desenvolvedor. Por exemplo, aqui está uma
função compute stats que recebe alguns argumentos. Na verdade, não temos ideia de que, se você observar a disfunção tratada, o que
devemos fazer com isso, existem usuários,
plantas e produtos, mas o que são essas coisas? Eles são dicionários
ou listas? É uma célula para um número? Nós simplesmente não sabemos. Se você fornecer dicas de tipo. Isso é muito mais fácil de ver. Podemos ver imediatamente o que
precisamos fornecer e o verificador de tipo
embutido
nos postes do VSCode. Mas outros editores também têm
um verificador de tipos como esse. Isso ajuda você a entender
onde estão as setas e resolvê-las antes
mesmo de precisar executar seu programa para descobrir. Portanto,
os tipos ajudam na legibilidade do seu código e também
ajudam a detectar erros
no início do processo de desenvolvimento As mãos digitadas
pelos indianos economizarão
muito tempo. Portanto, eu recomendo fortemente
que você comece a
usá-los mais, se ainda não tiver
feito isso. Então, isso conclui esta
lição sobre dicas de digitação. Na próxima lição, vou me
aprofundar nas aulas e mostrar algumas coisas
muito poderosas que você pode fazer com elas em Python.
5. Aulas de nível seguinte 1/4: Nesta lição, falarei sobre classes e mostrarei algumas coisas diferentes que você pode fazer com elas em Python. Então, eu tenho um exemplo muito
básico aqui. É uma pessoa de classe,
nada de especial. copo quase vazio
tem apenas um inicializador e recebe um nome
e um endereço. Como você pode ver, são cordas. Estou usando dicas de digitação aqui. Eu provavelmente deveria acrescentar, já que o inicializador não
retorna nada. E então eu estou usando uma função de
geração de ID. Você pode ver isso
funcional
aqui para gerar
algum tipo de ID. Então você pode imaginar que isso é como um banco de dados simplificado. E então temos um
nome e endereço, e também temos uma lista, uma lista vazia de endereços de e-mail. Então, esta é a classe
individual, Básica. E então eu tenho a
função principal em que eu crio uma pessoa com um nome
e um endereço, e então imprimo essa pessoa. Então, quando eu executar
esse código, é isso que vamos
obter resultados. Então, estou imprimindo um objeto pessoal. Você pode ver isso
aqui. Agora, essa informação
não é muito útil, certo? Porque simplesmente obtemos
algum endereço de memória. Não temos ideia do que
realmente está contido
no objeto Person seria bom se pudéssemos
realmente ver isso. E, claro, isso é
possível em Python. A principal forma de
fazer isso é
adicionando um método STR ou
wrapper dunder. Vou falar mais sobre
isso em um minuto. Mas, no geral, há
uma maneira diferente de definir classes em Python. No geral, isso é um pouco mais simples. Eu me sinto um pouco mais
fácil de ler,
usando classes de dados. Mas você poderia dizer que
as classes de dados são realmente mais orientadas para classes
orientadas a dados, certo? Pontos e
vetores de dados estruturados e coisas do tipo. Em vez de um botão ou uma superfície de pavimento ou mais
outra classe orientada ao comportamento. Mas ele adiciona muitos mecanismos
convenientes, como uma maneira muito mais fácil
de definir atributos. Você pode compará-los com
muito mais facilidade. Você pode imprimi-los com
muito mais facilidade. Então, essas coisas são úteis
para quase qualquer tipo de aula. Como
transformar pessoa em classe de dados? Na verdade, isso é muito simples. Então, o que vou fazer
é, a partir das classes de dados, importar a classe de dados, que é o tipo que
vamos usar aqui. E então, em vez de definir
uma pessoa dessa forma, vamos escrever uma classe
de dados acima dela. E agora o que posso fazer é especificar as
variáveis da instância aqui. Então, temos um nome,
que é uma string, e temos um endereço, que também é primavera. E teremos uma
lista de endereços de e-mail. Essa é uma lista de sequências, certo? E a identidade. Nós também podemos fazer, eu vou te mostrar
como fazer isso em um minuto. Mas se eu remover isso, agora você pode ver que
é muito mais fácil
entender o que uma
pessoa realmente é porque podemos simplesmente olhar o topo e ver imediatamente nomes
das variáveis de instância e seus tipos. E é por isso que
as classes de dados são realmente úteis. E eu voltarei
a isso em um minuto. Deixe-me comentar isso. Mas agora, se eu criar um
pessoalmente dessa forma, isso acontece
exatamente da mesma maneira. Portanto, as classes de dados
realmente geram um inicializador com base
nos valores dessas variáveis de
instância. Quando eu executo isso, você pode ver que agora imprime a pessoa. Mas você também vê
que a classe de dados já resolveu que, se você imprimir o objeto, imprimirá algo significativo, implementando os métodos wrapper dunder. falarei mais sobre isso
em um minuto. Então, como adicionamos essas
outras coisas à classe de dados? É claro que temos o
ID, que é uma string, mas agora gostaríamos de atribuir a ele um valor gerado
por essa função. Então, para fazer isso, precisamos usar
a função de campo que
também está nas classes de dados. E o que estou fazendo aqui
é que id é igual a campo e a fábrica padrão, que é
basicamente a
função que cria o valor para nós, será gerar ID. Então, agora o
que acontece é que, para
calcular esse valor, a classe de dados
chamará essa função
para computá-lo. E como isso agora
tem um valor padrão, temos que colocá-lo abaixo. As outras células não
têm um valor padrão. Então lá vamos nós. Agora, quando eu executar isso novamente, você verá que agora minha
pessoa tem nome, endereço, mas agora também tem
um id que foi gerado chamando
a função generate ID. Você também pode simplesmente fornecer
um valor padrão, por exemplo, digamos que temos uma variável de instância
ativa aqui que é booleana. E o padrão será
definir isso como verdadeiro. Então, agora uma pessoa está ativa
por padrão. E você vê que agora também está incluído no
objeto Person aqui. Agora, o que mais podemos fazer? Bem, também temos os
endereços de e-mail listados aqui, então vamos acrescentar isso. E então vamos também adicionar
um valor padrão aqui. Agora você pode ficar
tentado a escrever isso. Isso não é uma boa ideia porque a forma como o Python
funciona é que esse valor é realmente gerado uma vez
na interpretação. Portanto, se você atribuí-lo como um valor padrão,
esse é o mesmo
motivo pelo qual você não deve
atribuir uma lista vazia como um valor padrão em funções. Você pode ter um comportamento
inesperado porque todas
essas listas vazias basicamente
se referirão ao mesmo objeto
e você não quer isso. Então, o que você pode fazer
também é definir um campo aqui e simplesmente dizer
que o valor padrão, aquela fábrica padrão, será o inicializador da lista, assim. Agora, cada pessoa
também terá uma lista de endereços de e-mail que por padrão, ficarão vazios. Outras coisas que você pode fazer com classes
de dados que
são muito boas. Bem, uma é que, como vimos, ele gera o inicializador. Então, aqui temos uma pessoa. Portanto, ele tem um nome, endereço, ID, ativo e lista
de endereços de e-mail. Mas talvez você não
queira que o ID esteja lá, você quer que ele seja
sempre gerado. O que faz sentido, certo? Não sabíamos, não queremos
especificar esses IDs explicitamente. Queremos que ele seja
gerado automaticamente. Então, o que
você pode fazer é simplesmente fornecer ao campo
a função em que ela é
igual a false, assim. E agora, quando você olha
novamente para a definição
de Denise lives, você vê que o campo ID
não faz mais parte
do inicializador,
mas é não faz mais parte
do inicializador, claro que ainda
faz parte da pessoa aqui que vemos que está sendo
gerada automaticamente. Portanto, isso permite que você
tenha algum controle sobre a aparência do inicializador
e, ao mesmo tempo, tenha uma visão geral
muito boa de todos os atributos da
instância. Outra coisa que você
talvez queira fazer é adicionar uma
sequência de pesquisa a essa pessoa. Então, digamos que temos
uma string de pesquisa. Variável de instância,
sim, é uma string. E o que queremos agora é
que o valor aqui seja derivado do nome
e do endereço. Então é isso que
queremos ser capazes, Sergio. Mas é claro que aqui
ainda
podemos definir esse valor porque não
temos esses valores. Isso é simplesmente uma especificação
dos atributos da instância. Então, o que podemos fazer
em vez disso é simplesmente fornecer o campo e
defini-lo como falso porque
não queremos poder fornecer
a string de pesquisa como
parte do inicializador. Mas então o que podemos fazer
é definir um impulso nela. Os métodos Dunder não retornam nenhuma sequência de caracteres de pesquisa de pontos próprios igual. E então vamos simplesmente
construir a string self.name e o endereço de ponto automático, assim. Agora, o que acontece é
que ambos inseridos são chamados após a criação do objeto
. Então, é quando temos o valor do
nome e do endereço
e, em seguida, podemos calcular
a string de pesquisa. Então, quando executo esse código, novamente, você vê que agora temos
todos esses valores aqui, mas também vemos que temos
uma string de origem que é o nome mais espaço
e depois o endereço. Então, o que mais podemos fazer? Bem, talvez não queiramos que a sequência de pesquisa apareça quando imprimimos a
pessoa, porque isso é espécie de
inflamação dupla, certo? Você pode imaginar que, se
a classe de pessoas crescer, essa sequência
de pesquisa ficará muito longa. Então, em vez de imprimi-lo, toda vez que
imprimimos uma pessoa, também podemos adicionar aqui que um
rapper é igual a falso. E isso significa simplesmente que,
quando imprimimos a pessoa a sequência de
pesquisa não fará parte da representação. Não será incluído.
Isso é o que você vê aqui. E, para esclarecer, essa sequência
de pesquisa é na verdade, algo
interno à classe. Você também pode decidir colocar um sublinhado na
frente dele, dessa forma. E agora estamos fazendo essa
distinção de forma ainda
mais clara na própria
definição de classe. Outras coisas que podemos fazer é controlar como os objetos são criados. Então, aqui temos uma pessoa, fornecemos um nome e endereço, mas se você quiser, podemos remover esses argumentos, nomes de
palavras-chave aqui e agora, é claro, isso ainda
funcionará. É assim que as classes funcionam por
padrão em Python, certo? Mas e se você quisesse ter
certeza de que
só pode fornecê-lo usando argumentos de
palavras-chave que
você sempre precisa escrever, nome igual e endereço igual. Bem, você pode fazer isso passando um argumento aqui para o decorador da classe
date, e isso é
apenas uma palavra-chave e padrão, isso é falso, mas
podemos defini-lo como verdadeiro. E agora vemos aqui que, na verdade,
estamos recebendo um erro porque ele
espera palavras-chave. Agora temos que escrever o
nome é igual a John e o endereço é igual a
123 ruas principais. Para que isso funcione, isso não tem nenhum efeito, qual é o resultado real, mas afeta a forma como
podemos criar pessoas. E ter esse rigor extra às vezes
pode ser uma ferramenta útil. Outra coisa que você pode
fazer é congelar uma classe de dados. E isso significa que você pode modificar o objeto depois
que ele for criado. E fazemos isso fornecendo as passas congeladas dessa forma. Mas agora algo
interessante está acontecendo. Você vê que nossa ideia com a string de pesquisa
na verdade não
funciona mais porque, é claro agora temos
um objeto congelado, que significa que podemos modificá-lo, mas ainda o estamos modificando
depois de criado. Eu vou
te mostrar outra maneira de abordar isso em um minuto. Então, por enquanto, deixe-me deletar isso. Usaremos
outra coisa mais tarde para resolver
esse problema. Agora, quando executo isso, é
claro que ainda há trabalhos, mas agora, quando tento
modificar a pessoa, o nome do ponto da
pessoa é igual, agora você vê que na verdade
não temos permissão para fazer isso. A pessoa está congelada e recebe um
erro ao executar isso Você também vê que recebemos um erro de instância
congelada. Isso é muito útil
se você quiser
ter certeza de que, se estiver
usando alguns dados, não os
modifique acidentalmente. Mas como resolvemos esse problema de cadeia de caracteres de
pesquisa agora, porque não temos permissão para modificar esse objeto depois
que ele foi criado. Bem, uma coisa que você pode fazer
é usar propriedades em vez disso.
6. Aulas de próximo nível 2/4: Essa é outra adição muito
interessante às aulas que permitem
torná-las muito mais poderosas. O que você pode fazer é,
em vez de usar isso, usar uma propriedade
para definir um termo de pesquisa. Então, eu vou ter
uma propriedade aqui, e vamos chamar isso de cordão de
camisetas. É quase como um método. E isso retornará uma string. E simplesmente vou
remover isso aqui. E a string
de pesquisa será uma string f
contendo self.name, self.age, address, assim. Então, ainda está congelado. Mas
agora vamos remover isso novamente. Quando eu imprimo a pessoa. Bem, a string de pesquisa não
existe por causa da propriedade, mas nós a temos para que eu
possa imprimir ponto pessoal, sequência de
pesquisa, assim. Está reclamando que agora
é um membro privado,
mas, como você vê, ele realmente funciona em suas impressões digitais. Claro, se você quiser que a string de
pesquisa esteja mais disponível
publicamente, é claro
que você pode
remover esse sublinhado deste lado da seguinte forma. E agora que o erro dos postes
também ocorre ao nosso redor novamente, ainda
obtemos os mesmos resultados. Então, uma vez que adicionamos aqui está uma propriedade somente para leitura que
também é chamada de getter. E é lido apenas porque não
podemos atribuir nada a ele. Se eu fizer com que a
string de pesquisa por pontos por pessoa seja igual a alta, então você vê que recebemos um
erro aqui de que não podemos atribuir um membro a uma
propriedade que não é permitida. O que você pode fazer se quiser que
a string de pesquisa seja modificável é
realmente adicionar um setter. E como fonte, por exemplo ,
propriedade, isso é
muito fácil em Python. Simplesmente escrevemos a sequência de caracteres de
pesquisa ponto
e, em seguida, temos
aqui o setter de pontos. E então simplesmente definimos o
mesmo nome de propriedade novamente. Mas aceita um argumento
que é o valor. Esse caso é uma string. Vai retornar nulo. E então aqui
podemos definir o valor. Agora, é claro,
não vai funcionar porque nossas classes de dados estão congeladas, certo, então podemos
mudar qualquer coisa. Então, se removermos isso e
agora poderemos armazenar coisas. E como você poderia armazenar um valor no objeto,
se quisesse. Mas é claro que realmente não
precisamos disso no caso da string de origem,
porque estamos simplesmente retornando um valor computado aqui Tenho outro exemplo
aqui que faz um pouco mais de sentido em
termos de propriedade. Então, eu tenho um videoclipe de aula que tem minutos e
segundos, um título. E você vê que somos uma
propriedade chamada duração. E basicamente,
dependendo do número de
minutos e do número de segundos, retorna
um total de segundos. E então
também podemos ter um setter que define um número de segundos. E isso basicamente usa
a função div mod para calcular qual deveria ser
o
valor de minutos e segundos. Então, aqui você vê um exemplo
do uso de uma propriedade, um getter e um setter para controlar como os valores
estão sendo armazenados. Objeto, nesse caso, minutos e segundos
do videoclipe. E aqui temos um projeto de
vídeo que tem uma lista de videoclipes. E, novamente, esse é um campo usado como lista de fábrica padrão. Temos um
valor padrão para nosso título e também temos uma
propriedade na duração total, que é a soma de
cada um desses clipes, o número de
minutos e segundos. Então eu tenho uma função
principal simples em
que tenho dois clipes. Um videoclipe de
um minuto e 30 segundos, outro videoclipe
de 2 min e 30 segundos. E então eu crio um projeto
que tem esses dois clipes. Eu imprimo o comprimento total, mas depois uso o configurador de duração para
alterar o clipe, duração de 220 s, que na verdade
é 2 min. E então podemos imprimir o comprimento total do
projeto. E quando eu executo isso
, é isso que obtemos. Então, obtemos 240, mas depois
passamos de 1 min para 2 min. Então, são trinta segundos a mais. Então, são 270 s
que estão sendo impressos. Ou você também pode ver que,
se eu imprimir o clipe aqui, Friends recorta um e eu
executo esse código novamente. Você vê que depois de
atualizar o clipe, você vê que
temos 2 min e 0 s. E é exatamente isso que
nossa dose de duração cetera. E se você
olhar para o projeto de vídeo, podemos realmente
simplificar isso ainda mais, porque aqui estão os minutos de pontos do
Eclipse, bloqueia os segundos de pontos do clipe. Bem, podemos simplesmente usar a duração que a leva até aqui
para torná-la ainda mais curta. E então obteremos exatamente o mesmo
resultado de antes. Então, essas são propriedades que
podem ser úteis para obter um valor computado sem realmente ter que
armazenar esse valor. E também ajudam a ocultar alguns dados de baixo nível em uma classe, como se você tivesse
minutos e segundos internamente, mas simplesmente usasse os
segundos finalizados externamente, um criador e configurador
de propriedades o
ajudarão a conseguir isso.
7. Aulas de próximo nível 3/4: Quero falar sobre mais duas coisas antes de terminar esta lição. Uma é string versus wrapper. Então, vimos que, se você tem uma classe de dados como
essa classe pessoal
e imprimimos a pessoa que
vemos, obtemos uma espécie de versão amigável para
desenvolvedores do
que a pessoa realmente é , útil para depurar ou se você quiser bloquear
algo ou qualquer outra coisa. E é exatamente isso que o
wrapper deve fazer. O Wrapper deve fornecer uma
representação amigável de um objeto para desenvolvedores. A ideia é até mesmo
que você
possa pegar essa representação armazenada em um arquivo e ela deve conter
informações suficientes para depois, se você quiser lê-la de um arquivo novamente e recriar o objeto que é
diferente de ter uma
representação fácil de usar. Isso não é algo que
você queira que um usuário veja porque é meio difícil
de ler e escrever. E é aí que o método
dominante da string é quatro. Portanto, se você também quiser ter uma
representação mais fácil de usar de um objeto, você pode realmente usar os métodos
string dunder. classe de dados não adiciona isso, mas você mesmo pode adicioná-la simplesmente adicionando os métodos
string dunder. E é claro que isso vai
retornar uma string, certo? E o que queremos devolver? Bem,
digamos que simplesmente queremos imprimir o nome da pessoa. Então esse é o
nome próprio, assim. Agora, quando executamos
esse programa novamente, você vê que ele agora simplesmente
imprime o nome. E isso porque string
dunder substitui rapper, certo? Então, se você não tem uma string, mas tem um rapper, o Python usará o wrapper. Se você tiver uma string
, ela a
usará em vez disso. Mas e se você ainda quiser imprimir a
versão embrulhada da pessoa? Bem, há algumas maneiras
de fazer isso. Uma é que você pode usar a função
wrapper dessa forma. E isso simplesmente chamará
o método wrapper dunder e o retornará como uma string
que agora podemos imprimir. Então, se eu fizer essa margarida, teremos nossa pessoa, uma pessoa amigável para
desenvolvedores. Novamente, o que você também pode
fazer é usar cordas. Então, se você tem uma
string f como essa, e agora, é claro, simplesmente
imprime a pessoa. Então, novamente, isso imprimirá
o método string dunder, mas você pode realmente
usar o ponto de exclamação R. E em uma string f,
isso também
imprimirá a versão em invólucro dos objetos que você tem algum controle sobre o tipo
de coisa que está sendo impressa, mesmo
que você tenha
talvez os métodos wrapper e string dunder. É bom saber.
8. Aulas de próximo nível 4/4: A última coisa que
quero mostrar é uma maneira simples de melhorar
a velocidade de acesso aos
elementos em seus objetos. E isso é usando slots. Normalmente, o Python usa
um dicionário para armazenar os valores das
variáveis de instância de um objeto. E o acesso ao dicionário é rápido, mas não é incrivelmente rápido. E a razão pela qual não é tão rápido é porque é
muito dinâmico. Você pode adicionar chaves e
valores a qualquer momento. Assim, você pode basicamente
decidir
modificar dinamicamente o objeto e adicionar novas
variáveis de instância a qualquer momento. Python é uma linguagem muito dinâmica. Lembre-se de que, com os slots, você concorda com o intérprete que
será um pouco mais rigoroso. Você terá um
conjunto fixo de variáveis de instância. E por causa disso, Python pode otimizar
as coisas e
acessá-las muito mais rápido. Agora, na maioria dos casos, você
realmente
terá um número fixo de variáveis de
instância. Você não quer mudar seus objetos o tempo todo, em
todo lugar, certo? Vai se transformar em uma bagunça. Então, se você fizer isso e usar muito, na verdade será
muito mais rápido. Então, um exemplo muito simples aqui que novamente usa classes de dados, que tem uma opção de slots. Portanto, se você
tiver uma classe de dados, pode simplesmente definir os
slots como verdadeiros, como o que estou fazendo aqui. E então ele vai usar slots em vez do dicionário
tradicional. Então, eu tenho uma versão
da classe pessoal, não usa muitos e uma versão da primeira classe
que os usa. E então eu tenho uma função aqui, chego na elite que recebe vagas para uma
pessoa ou uma pessoa, e isso vai
definir o endereço, ler o endereço e excluir o endereço apenas como
uma espécie de referência. E então eu tenho a função
principal de criar
algumas pessoas, pessoas caça-níqueis e
pessoas normais. E então eu estou usando o
timeit to repeat para chamar a
função get set delete na pessoa. Muitas vezes, neste
caso, 1 milhão de vezes. Na verdade, eu pego a
mediana disso para obter o desempenho médio
e vou
imprimir quanto tempo.
Tudo bem, as
melhorias de desempenho. A propósito, essa
coisa aqui é parcial. Eu vou falar sobre
isso na próxima lição. Portanto, fique atento a isso. Então, deixe-me fazer isso agora que
podemos ver o que está acontecendo. Agora vemos que,
quando não temos slots fazer todas essas operações de acesso, leitura, gravação e exclusão
leva 0,06 s com slots, leva 0,05 s.
Então,
isso representa uma
melhoria de quase 20% no desempenho, especialmente se você estiver
lidando com muitos dados, isso realmente
terá um grande impacto se você estiver fazendo
essas operações em qualquer lugar. Portanto, é muito simples,
basta definir os slots como verdadeiros
ao definir
sua classe de dados. Agora você pode se beneficiar
dessa melhoria, pois
os slots de melhoria de velocidade
também usam um pouco menos de memória. entanto, existem algumas
ressalvas. Uma é que, se você estiver usando
vários slots de herança , pode causar problemas. Portanto, tenha muito cuidado com
isso, mas você deve, em qualquer caso, ter cuidado ao
usar herança múltipla. E outra coisa é
que você os conta adiciona ou remove
dinamicamente variáveis de
instância, dois objetos, porque com slots estamos sendo um
pouco mais rígidos. Mas acho que, no geral, isso é
realmente uma coisa boa. Então, isso conclui esta lição em que
mostrei algumas coisas
mais poderosas que você pode
fazer com classes em Python. Na próxima lição,
falarei sobre o que você pode
fazer com as funções. E também vou
examinar mais de perto
algumas ferramentas funcionais em
Python para ajudá-lo a fazer isso.
9. Funções de nível seguinte 1/4: Nesta lição, quero falar sobre funções e algumas
das coisas mais poderosas que você pode fazer com funções em Python, semelhantes ao que fizemos com
as classes e com a lição anterior. Agora, em Python, basicamente, tudo é um objeto, certo? Temos números inteiros, strings, classes das quais você
pode criar objetos. Até mesmo uma função é um objeto, é um objeto do tipo double. E você poderia, na verdade,
se quisesse criar uma classe que tenha uma
chamada de métodos dunder, essa classe também é uma classe de retorno de chamada que também
se comporta como uma função. Na verdade. Um exemplo muito simples aqui, eu tenho uma classe de cliente, que é uma classe de dados. Se você está dormindo com um nome, uma idade e sua
função aqui, chamada promoção de
Enviar e-mail,
que recebe uma lista de clientes e é
usada para
analisar os clientes
que verificam o cliente. Se a idade for de cerca de 50 anos, o cliente está
qualificado para a promoção. E se a
idade do cliente estiver acima de 50 anos
, ele estará
qualificado para a promoção. E dependendo desse valor, imprimimos algo
diferente na tela. Então, eu tenho uma função principal
que cria uma lista de clientes e envia promoções
por e-mail para
esses clientes.
Pelo menos aqueles com mais de
50 anos contornam isso, então é isso que estamos recebendo. Então, ele verifica cada cliente e depois imprime
onde o cliente está qualificado ou não para enviar promoção
por e-mail, que é
uma função que pode ser chamada, que obtém uma lista de clientes
e devolve, nenhuma. E, claro, esse
é um uso muito básico do funcional em Python, certo? Mas como a
função é um objeto, você pode realmente fazer muito
mais coisas com ela. Por exemplo, você pode
passar uma função para outra função ou até mesmo fazer
com que uma função retorne outra
função como resultado. E nós fazemos isso. É
quando você usa algo chamado funções de
pedidos superiores, por exemplo ,
aqui, verificamos
se a
idade do cliente é de cerca de 50 anos
, ele está
qualificado para a promoção. Mas talvez queiramos fazer uma verificação mais complexa aqui que também verifique as coisas
do cliente. Agora, é claro, poderíamos
estender a declaração if aqui com cada vez mais
complexidade da condição. Mas também
seria bom que a promoção
por e-mail pudesse, de alguma forma, obter um mecanismo para verificar a elegibilidade
chamando outra função. E é isso que podemos fazer
com funções de ordem superior. Então, o que podemos fazer em vez disso é
definir que uma função aqui é elegível para promoção E a
função atrairá um cliente. E ele
retornará um booleano. E isso simplesmente fará com que o ponto h
do cliente esteja acima de 50. Como usamos essa
função aqui? Bem, podemos simplesmente
chamá-lo, certo? Poderíamos fazer isso se fosse
elegível para promoção. Cliente, assim. E então, quando executarmos
isso e depois o que obteremos
exatamente os mesmos resultados. Mas isso também nos dá muito pouco controle, porque agora não
há como definir outras funções
e
determinar dinamicamente se devemos enviar um e-mail aos clientes ou não. É sempre uma função
específica do disco. Então, em vez de
chamá-lo diretamente aqui, também
podemos apresentá-lo como um argumento para enviar uma promoção
por e-mail. Então,
digamos que a função é elegível para escrever
isso corretamente. Obviamente, como
isso é uma função, esperamos algo do
tipo objetivo, que é uma função. E como especificamos que
tipo de doença funcional? Bem, isso é usar
os colchetes e depois usamos outro par de colchetes para indicar quais são
os argumentos
dessa função com os tipos de parâmetros. Então, o que essa expressão é um cliente e o que ela
retorna é booleano. E então, em vez de chamar a disfunção
diretamente aqui, chamamos a função que
passamos como um parâmetro da seguinte forma. E agora, é claro, temos que ter
certeza de que, quando
realmente chamamos a função aqui, você vê que também há um erro
aqui que realmente passamos. Essa função específica é elegível para promoção. Assim. Agora, quando executamos isso, obtemos exatamente o mesmo resultado, mas agora o
dividimos de forma um pouco diferente, e agora estamos usando funções
de
ordem superior para dividir a promoção de envio de e-mail e
a função de verificação de elegibilidade. Agora, em vez de precisar
definir uma função como essa, também
há
outra coisa que você pode fazer que é usar uma função
lambda. função lambda em Python
é uma função anônima. Então, aqui não temos uma função anônima porque
essa função tem um nome. função Lambda não
tem um nome e você especifica diretamente
como uma expressão. Então, em vez de ter
essa função aqui, à
qual nos
referimos pelo nome, podemos usar uma
função lambda. Isso vai atrair um cliente e ele retornará c, H é pelo menos 50. E agora, quando executamos isso
, obtemos novamente
exatamente o mesmo resultado. Mas agora estamos usando uma função lambda
anônima. E isso é bom
porque agora temos uma maneira
muito fácil de mudar a forma como as
promoções por e-mail são tratadas com código de
erro, por exemplo, também
podemos torná-la
uma idade diferente, por exemplo, digamos que apenas acima de 60 anos. E agora vamos
obter um resultado diferente. E não precisávamos
mudar nada na função de
promoção Enviar e-mail, o que é muito bom.
10. Funções de próximo nível 2/4: Vamos tornar esse exemplo
um pouco mais complicado. Vou remover a função
Lambda aqui novamente, e vou ligar para se qualificar mais uma
vez e vou
mostrar mais uma maneira de ter
mais controle sobre o que acontece
quando você chama uma função. Agora, é claro que, se
tivermos ,
é elegível para promoção, ,
é elegível para promoção, não temos como
realmente controlar esse valor porque ele está
codificado na função. Portanto, talvez queiramos
ter uma opção aqui para ter uma idade limite, que será um número inteiro. E então, em vez de
usar o ano 50, podemos usar a cruz
de oito, certo? Então, isso é muito bom. Mas agora temos um problema aqui que é elegível
para promoção. É claro que não adere mais
ao tipo dessa
ingênua que tínhamos aqui. E isso porque agora existe esse parâmetro extra
que precisamos fornecer. Você pode fazer uma
coisa que é, por exemplo ,
fornecer, um valor padrão como esse. E então isso é
uma espécie de solução alternativa, porque
agora funciona novamente. Mas agora ainda não há
como alterar a idade aqui de forma alguma, porque só
podemos usar o valor
padrão, certo? Agora, é aqui que o pacote de
ferramentas do funk pode
nos fornecer uma boa solução que é chamada de aplicação de
função parcial. O que significa
aplicação da função de parcela? Basicamente, significa que se
você tem uma função com alguns parâmetros diferentes,
como o que temos aqui, é que com parcial, você
já pode aplicar alguns
desses argumentos. E então você recupera
outra função. Quando você chama essa função
, os argumentos
que você aplicou anteriormente serão usados. Portanto, ele permite que você altere a
assinatura
do cabeçalho da função
já fornecendo alguns dos valores. Então, por exemplo o que podemos fazer aqui, vamos
primeiro importar das ferramentas, que é o modelo que
vamos usar. E então o que você pode fazer é remover esse
valor padrão aqui novamente, para que eliminemos o erro. Você pode fazer agora é dizer,
bem, nós temos, é elegível. E digamos que queiramos
cortar em 60, então vamos
chamá-lo de elegível de 60, que é uma inscrição
parcial ou é elegível para promoção. Mas vamos
fornecê-lo com uma idade limite de 60 anos. E agora, quando enviamos uma promoção
por e-mail, podemos enviar, podemos fornecer a ela a função elegível de 60 parcialmente
aplicada. Então, parcial recebe uma função. Ele aplica um valor a um de seus argumentos e
retorna uma nova função, que agora
é chamada de elegível 60. Infelizmente,
mergulhe aqui não preciso qual
é o tipo dessa função parcialmente
aplicada. Talvez em uma versão futura
do Python que venha, mas isso é novamente uma função e agora está sendo chamada por promoções enviadas por e-mail. Quando eu executar isso,
você
obterá clientes com mais
de 60 anos. Portanto, este é um exemplo de aplicação de função
parcial que é realmente poderosa e
permite modificar funções, simplificá-las e usá-las, tornando-as compatíveis
com outras áreas do seu aplicativo já
aplicando alguns dos valores.
11. Funções de próximo nível 3/4: Outra coisa interessante que você
pode fazer com as ferramentas func é a chamada propriedade em cache. Você deve se lembrar do exemplo
que mostrei em uma
das lições anteriores em que eu tinha uma pessoa e tínhamos uma propriedade de string de
pesquisa, que era uma propriedade
computada. Então, toda vez que você
liga para a propriedade, o valor é computadores. Talvez nem sempre seja isso que você deseja se o valor de
calcular um três, complexo de computar,
bem, com a
string de pesquisa, não fosse tão ruim. Mas se for realmente complexo,
evite ter que
computar isso toda vez que
ligar para a propriedade. Uma maneira de resolver isso
é realmente armazenar o valor no objeto que você
calculou ao
criar o objeto. Mas se você estiver usando uma classe de dados congelada,
isso não é possível. É aqui que a propriedade em cache entra em ação a partir das funções. Então, aqui eu tenho um exemplo
de como ele está sendo usado. Eu tenho um conjunto de dados de classe que
tem uma sequência de números. E nesse caso, estou simplesmente armazenando
isso como uma tupla em uma variável de instância de dados. E então eu tenho a propriedade em dinheiro, que é o desvio
padrão desses dados específicos. E estou usando a função de
desvio padrão das estatísticas. E depois da
função principal, onde basicamente cria uma instância
desse conjunto de dados. E então eu imprimo
o desvio padrão. E o que é realmente interessante é que, se eu executar isso agora, você vê que só obtemos
uma vez as impressões dessa mensagem computando o desvio
padrão. E isso porque
isso está sendo armazenado em cache. A propriedade é calculada
uma vez e toda vez que você chama posteriormente, simplesmente
usando o valor em cache. Portanto, seria muito poderoso se eu usasse uma
propriedade normal para isso. Assim. E agora
eu executo isso novamente, você vê que o desvio
padrão é calculado três vezes. Essa é a diferença.
Então, vou transformar essa propriedade real em dinheiro. E agora, quando executamos isso novamente, ele é novamente computado apenas uma vez. Portanto, você usa uma
propriedade em dinheiro para memorizar determinado valor, para não precisar computá-los
repetidamente, mas ainda assim ter a
flexibilidade de
acessá-los como uma
propriedade normal em seu objeto.
12. Funções de nível seguinte 4/4: última coisa legal
que quero mostrar a vocês fontanelas é o envio único. Single Dispatch é um
decorador que você pode usar para ter uma espécie de sobrecarga de
funções. Então, o que você faz é definir uma função genérica
e
variedades de registro de dados dessa função que podem lidar com diferentes tipos. Aqui está um exemplo. Eu tenho uma única função de despacho chamada add que tem um x e y, dois números inteiros, e ela retorna
a soma de dois inteiros. Mas então eu adiciono uma segunda
versão dessa função. Então, essa é, digamos,
a versão padrão da função
que recebe números inteiros. O segundo que recebe
cordas e cordas. Não quero adicioná-los
diretamente assim. Eu quero adicionar um espaço. Então, eu estou usando a
formatação de strings aqui para inserir um espaço entre
o x e o y. Vou voltar
a eles em um minuto,
mas se tivermos aqui
imprimido mas se tivermos aqui 12 e
imprimindo hello world, então você vê que a
primeira linha que será impressa é de fato a soma e as sequências concatenadas com o
espaço entre elas. E há mais coisas que você pode fazer com isso. Por exemplo, você também pode usar o tipo de união sobre o qual
falei
no vídeo de digitação. Então, aqui eu registro uma versão
dessa função que
recebe um x e um y, que é uma lista ou um conjunto. E isso retorna uma lista, simplesmente encadeando os elementos
na lista de conjuntos. E também há uma forma
funcional. Então, esses usam o chamado decorador com
o sinal de adição na frente. Mas você também pode simplesmente chamar o registrador de pontos como uma função e fornecer o tipo e , em seguida, a função que
deve lidar com isso. Então é isso que você
vê sendo feito aqui. Então, posso simplesmente executar isso novamente
e a lista de devolução da OCDE. E o último retorna uma tupla porque foi isso que
especificamos aqui. Emita uma tupla mais rápida e você
também retornará uma tupla. Assim, você pode usar o
envio único para lidar com objetos de diferentes
tipos sem problemas em Python. Agora, para ser honesto, eu não
usei muito isso, mas acho que é útil
saber que isso existe. E, em alguns casos
, pode ser útil
simplificar um pouco seu código. Então, no geral, acho que
a biblioteca de ferramentas do funk é uma
coleção de ferramentas muito interessante. Também há algumas outras
coisas que
não foram abordadas nesta
lição de hoje. Você pode dar uma
olhada na documentação. Mas, no geral, acho
que é muito interessante. Como também notei, muitas pessoas tendem a
se limitar às aulas, enquanto as funções também são
muito poderosas. Portanto, eles também costumam
permitir que você simplifique seu código em vez de
usar classes em qualquer lugar. Então, da próxima vez, você estiver escrevendo um programa em Python e lidando com
classes complexas e tudo mais. Pense em como
você pode
transformar isso em uma versão mais funcional. E muitas vezes você notará
que o código será mais curto e fácil de ler. Então, essas são ferramentas divertidas e uma espécie de
programação funcional em Python. Na próxima lição,
vou dar uma olhada outro conceito que você
quer achar realmente útil, que é a programação simultânea.
13. Programação simultânea 1/3: Especialmente se você está começando a usar o Python de forma mais profissional, repente
você vai
interagir com uma API, um banco de dados, qualquer coisa
basicamente em uma rede. E se você fizer
isso, significa que se torna importante
para seu aplicativo
lidar com esses tipos de
solicitações com eficiência. Porque se você não fizer isso,
seu aplicativo ficará muito lento. Em Python, você pode usar o pacote de IO assíncrono
para ajudá-lo com isso. E isso depende de algo chamado programação
simultânea. Você já deve ter ouvido o termo simultaneidade de programação simultânea antes e o
termo relacionado paralelismo, programação
paralela. Essas coisas, na verdade,
não são a mesma coisa. Existem diferentes.
A diferença é que por meio da
computação paralela, você realmente tem unidades de processamento
paralelo separadas para realizar tarefas separadas. Então, se você tem um
aplicativo que faz várias coisas ao
mesmo tempo, essas coisas estão
realmente sendo feitas em paralelo ao mesmo tempo. Isso é paralelismo
e simultaneidade significa que um aplicativo está progredindo em várias
tarefas ao mesmo tempo. Mas, em vez de ter
verdadeiras operações paralelas, ele realmente alternará entre essas tarefas de forma dinâmica. Então, digamos que um aplicativo
da tarefa a para a tarefa B, ele simplesmente começará a fazer um
pouco sem parar com
a, continuará com B, um pouco de B e depois voltará para a e assim por diante. Portanto, não é paralelo, mas é simplesmente alternar
entre os dois. Porque isso acontece muito rápido. Como usuário, você pode ter a ideia de que isso
acontece em paralelo, mas na verdade
acontece simultaneamente. É simplesmente uma troca. É como a diferença entre as linhas na frente de um
caixa paralelo. Isso significa que temos
vários caixas e cada caixa tem uma linha
que está processando. Concorrência significa que
há um caixa, mas há várias linhas e todo mundo está se revezando. As linhas nesse caso
são as tarefas que um computador deve resolver e
os caixas são as da CPU. Os computadores modernos
usam uma combinação de paralelismo e simultaneidade. Você sabe, sua CPU pode ter 246810 núcleos que podem
fazer coisas em paralelo, mas seu sistema operacional terá dezenas a centenas de tarefas
diferentes. E ele
executará um subconjunto
dessas tarefas paralelamente
nesses diferentes núcleos de CPU, mas também
alternará entre
elas simultaneamente. paralelismo em Python
tem uma ressalva de que o Python tem o chamado bloqueio
global do interpretador. E isso significa que se
você tiver, digamos, vários segmentos que
deveriam ser paralelos,
que poderiam ser paralelos em Python, na verdade, isso não funciona
porque eles estão bloqueados para o interpretador. Há razões para isso. Eu não vou falar sobre isso
nesta aula. Mas as consequências
disso, por meio do paralelismo em Python, não são
realmente possíveis. Há algumas
soluções alternativas, por exemplo, você poderia, em vez de
ter um único processo, usar o
backedge
de multiprocessamento para criar
vários processos. E Dan, você
tem paralelismo. Você também pode mudar para um
interpretador Python diferente que não tenha esse bloqueio de
intérprete global. Mas, na verdade,
não há muitos
casos em que você precise
passar pelo paralelismo. Muitas vezes, a simultaneidade
já é muito boa e Python tem um
suporte muito bom para simultaneidade, especialmente desde o Python 3.7, que melhorou muito o pacote
Async IO. Agora, a simultaneidade é muito
importante porque isso
permite que você tenha
várias tarefas, por exemplo, uma tarefa para recuperar
alguns dados de uma API. Enquanto você espera pela resposta
da API, você já pode mudar
para outra tarefa, e isso também é útil, por exemplo, se você tem um aplicativo de GUI e a GUI está esperando que você digite texto nos
campos de texto ou pressione um botão. Bem, como é um programa
simultâneo, você pode realizar outras tarefas ao
mesmo tempo, como limpar
parte da memória, fazer uma pré-busca
de dados ou o que
quiser fazer para o aplicativo
funcione com mais facilidade. E dado que quase
todos os aplicativos hoje em dia se comunicam pela Internet e recuperam vários dados. É muito importante
lidarmos com a simultaneidade de maneira adequada. E é isso que o pacote
Async IO em Python permite que você faça.
14. Programação simultânea 2/3: Há duas
palavras-chave importantes que você deve saber escrever código
simultâneo ou assíncrono, e isso é esperar por async. Você pode escrever async na frente de uma função ou
método para indicar que essa será uma função ou método que você pode
chamar simultaneamente. E você pode usar um peso para indicar na frente
de uma declaração que a próxima declaração
deve esperar até que a
declaração anterior seja concluída. E essa é a
segunda parte que também é muito útil, porque muitas vezes você precisa esperar por uma determinada resposta
para fazer alguma coisa. Por exemplo, se você recuperar
dados de uma API, precisará esperar até
ter dois dados de volta para poder realmente fazer
algo com esses dados. Então async e await têm um exemplo
muito simples aqui. Você pode ver que
temos uma função de obtenção de Pokémon que usa a
API Pokémon para obter nomes de Pokémon. E estamos passando,
nesse caso, um id e
temos um URL para o qual
fornecemos o ID. É assim que
devemos chamar essa API. E então eu estou voltando para
aguardar HTTP GET. E então a URL http get é uma função auxiliar
que eu criei. Isso é assíncrono. Eu vou te mostrar como isso
funciona em um minuto. Mas aqui vemos a função
assíncrona Get Pokemon que
retorna os pesos
e, em seguida,
recupera os dados da URL. E usamos um garçom
porque é claro que
só podemos retornar quando
tivermos esses dados. A função principal aqui, como você pode ver aqui,
também é assíncrona porque, bem, ela usa a
função rent int para criar um número inteiro aleatório entre um
e o maior ID de Pokémon, que atualmente é 898. E então eu uso um peso para pegar o Pokémon
desse ID específico, e então imprimo o
nome do Pokémon. E aqui, o peso também é muito importante, porque é
claro que tenho
que esperar até obter o valor do Pokémon para poder imprimir o nome. Isso é o que você vê aqui. Eu dei uma olhada nisso. Veja, pegamos alguns Pokémon aleatórios
e você vê que
demorou cerca de meio
segundo ou segundo para obter esse
valor específico de Pokémon. Vamos tentar isso de novo. Você vê que demora um pouco e
então obtemos o resultado. Isso é falso porque
temos que esperar a API nos responda
e isso pode levar algum tempo. Aqui temos outro
exemplo que mostra como é
valioso usar a programação
simultânea. Então, o que eu fiz aqui foi ter
duas versões de como fazer isso. Receba a solicitação da API. Eu tenho uma versão assíncrona e uma versão
síncrona. Aqui você tem uma função que usa a versão síncrona. Então, o que estou fazendo é obter Pokémon
desse URL específico. E aqui eu não tenho uma versão
assíncrona que receba esse Pokémon. E nesse caso eu uso a função
HTTP GET assíncrona. Na minha função principal, tenho duas versões
após chamadas síncronas. Então, isso basicamente pesa cada vez para os resultados, certo? Então eu faço um for-loop e
obtenho 20 nomes locais aleatórios, e então eu calculo
o tempo total. E na chamada assíncrona, estou usando algo chamado
Async IO dot gather. E o que isso faz é que me
permite fornecer chamadas e várias
chamadas assíncronas, depois lançará elas
sempre querem formigas, em vez de esperar que cada mês termine antes de começar
o próximo coleta
assíncrona de aorta nos permite executar todas essas coisas
simultaneamente. Então, quando você executa isso, você vê isso enquanto esta
é a primeira vez. Agora você viu que,
no caso síncrono, demoramos quase 2 segundos para
conseguir esses 20 nomes de Pokémon porque esperamos
pelo primeiro antes de solicitarmos
o segundo. E no caso assíncrono, levou apenas 0,9 s, então isso é menos da metade. E isso porque estamos
executando as coisas ao mesmo tempo. Não precisamos esperar pelos resultados
da
primeira chamada de API. Para iniciar a segunda chamada de API, é
preciso ter cuidado, que geralmente essas
APIs têm limites de raid. Portanto, você não quer
lançar cerca de 1.000 chamadas ou desejos de
API porque
isso não será aceito. Portanto, você precisa fazer a solicitação da API dentro de
certos limites de taxa, como um número fixo de chamadas, como o número máximo
de chamadas por segundo, depende da API,
deve verificar isso. Mas, como você pode ver
, pensando um pouco sobre como podemos executar nosso
código de forma assíncrona, como ele pode ser executado simultaneamente. Isso nos poupará
muito tempo de espera e
fará com que nosso aplicativo funcione com muito mais facilidade. Então, isso está reunido, reunido
Basicamente, o que você vê aqui, estou descompactando uma lista de diferentes chamadas de
funções assíncronas. Então isso é async io.gov, que é uma ferramenta muito útil
para executar coisas simultaneamente. E tudo o que podemos ver aqui
nesses exemplos é que temos essa função principal assíncrona, mas a executamos usando o
async IO dot rum, que diz ao
interpretador Python que execute essa função principal de
forma assíncrona. E é necessário, porque
se eu simplesmente escrever main
, na verdade isso não
funcionará conforme o esperado. Porque se eu operar esse ar condicionado, receberemos todos os
tipos de avisos de que a co-rotina do Maine nunca
foi esperar. E isso é exatamente, claro,
o que está acontecendo aqui. Estamos simplesmente ligando para isso. Então, se você quiser
evitar isso,
então você deve chamar async IO dot run e escrever essa doutrina corretamente, é
claro. E então chamamos a função
principal assim. Quando eu executo isso
novamente, você vê que agora não temos mais o
erro.
15. Programação simultânea 3/3: A última coisa que
quero mostrar é como transformar código síncrono, código não simultâneo, em código
que é executado simultaneamente. E isso pode acontecer
às vezes, você pode ter um pacote de biblioteca que está usando e não é assíncrono, mas você quer
transformá-lo em uma chamada assíncrona. Como você faz isso? Então, eu tenho um exemplo aqui. Portanto, há uma
função assíncrona aqui, contador, e também há uma função de
solicitações de envio
síncrono aqui. E isso é simplesmente
usar o pacote requests write requests,
não o pacote de solicitações na verdade, não é assíncrono
e síncrono. Isso significa que, se
enviarmos uma solicitação aqui, precisaremos aguardar o resultado e simplesmente retornar
o código de status. Nesse caso, tenho
minha função principal aqui e estou enviando uma solicitação
para que nossos álcoois tenham desaparecido, que é meu site. E então eu estou imprimindo, recebi
a resposta HTTP com esse status e
ligo para o contador. Outra coisa é, claro, por que devemos esperar
pela resposta aqui
para iniciar o contador? Isso é tudo. Preciso disso, certo? Então, se eu executar isso, você vê
que enviamos a solicitação, obtemos a resposta e começamos a contra-atacar. E se quiséssemos
usar o gather para iniciar um contador ao mesmo tempo em
que enviamos a solicitação. Como fazemos isso? Bem,
se quisermos fazer isso, significa que precisamos ativar a solicitação de
envio e assim por diante. Função simultânea assíncrona em vez de uma função síncrona, se você quiser fazer isso, isso
é realmente muito fácil. Então, em vez de chamar solicitações de
envio como esta, que você pode fazer é
assincronizar o ponto IO em dois segmentos. Isso vai
transformá-lo em um tópico? E então estamos
ligando para enviar solicitação. Também precisamos fornecer os
argumentos, que é o URL. E então precisamos escrever um
peso na frente dele porque a ameaça transforma essa função em uma função assíncrona. Então, deixe-me fazer isso e você vê que ainda obtemos
exatamente o mesmo resultado, certo? Porque não temos peso. Então, enviamos a solicitação, obtemos a resposta e
iniciamos o contador. Mas agora que temos a versão
assíncrona aqui, podemos realmente usar a
coleta de pontos
assíncrona para fazer as duas
coisas ao mesmo tempo. Então, aqui temos um exemplo
em que eu configurei isso. Então, o que eu fiz
foi criar agora outra função chamada solicitação assíncrona
enviada que usa
um ponto de IO assíncrono para ameaçar chamá-la de forma assíncrona. E então, na minha função principal, eu não tenho coleta de pontos IO assíncrona. E então eu enviei
a solicitação assíncrona ou que causa disfunção, que costuma ameaçar transformar isso em uma função
assíncrona. E eu também estou usando o contador. E quando eu executar isso, você verá que
temos o contador. E começa ao mesmo
tempo em que envia uma solicitação. E então esperamos e, no
final, obtemos a resposta. Então isso é IO assíncrono. A propósito, aqui tínhamos nosso pessoal assíncrono do Get Pokémon
que estava usando esse HTTP GET, que é uma
função auxiliar que eu criei. Na verdade, isso está neste arquivo. E você vê que usamos Async IO dot two
thread aqui para transformar
a função request dot gets em uma chamada de função
assíncrona. Então, isso usa exatamente
o mesmo mecanismo para concluir que o IO assíncrono é
um recurso muito poderoso do Python. A programação simultânea em
geral é muito útil, especialmente se você estiver se
comunicando em uma rede com outros serviços e quiser que seu aplicativo se
comporte sem problemas. Então, espero que esta lição tenha lhe dado algumas ideias de como você pode pegar seu próprio código e transformá-lo
em código assíncrono, especialmente se você estiver
fazendo muitas dessas
comunicações de API ou banco de dados. Nas poucas
lições restantes deste curso, às vezes
eu poderia me referir
à programação simultânea. Mostre como você pode usar
esse recurso em particular, com a
programação simultânea em mente. Agora, a próxima lição
serão os iteradores.
16. Iteradores 1/3: Nesta lição,
falarei sobre iteradores em Python. O que não é iterador,
embora seja basicamente um objeto que pode
ser iterado, você pode
percorrer todos os valores. Você pode reconhecer um
iterador em Python porque ele implementa
o protocolo iterador, e isso significa que ele tem
os próximos métodos de dominó. Os iteradores estão por
toda parte em Python, são usados para fazer loops,
usam compreensões de listas
e em usam compreensões de listas muitos outros lugares em que
você provavelmente já
os usou algumas vezes sem nem mesmo perceber que
eram iteradores. Você pode ter ouvido
os termos
iterador e iterável. Eles não são a mesma coisa. Um objeto iterável que pode
fornecer um iterador. E isso significa que ele tem o método itr dunder
porque ele fornece um iterador. Um iterador próximo ao método doador
itr também tem um próximo método dominante
que nos permite obter o próximo elemento
na iteração. Alguns desses iteradores
são finitos, por exemplo, você pode iterar por meio de
uma lista fixa de itens. Alguns desses iteradores
são infinitos. Por exemplo, você pode iterar
todos os valores inteiros. Obviamente, em Python,
não há valor máximo para um número inteiro, pelo
menos algumas considerações de que memória do seu
computador é limitada. Então, listas, tuplas,
dicionários, conjuntos, cadeias de caracteres, essas são coisas
que são todas iteráveis. E você pode obter um iterador
deles e, em seguida, você pode iterar por meio de
uma sequência específica. Vamos dar uma
olhada em alguns exemplos. Eu tenho uma função principal aqui, e há um item de classe que tem um nome e um peso
para cada item ter peso. Obviamente, estou usando
classes de dados. E depois da
função principal, qualquer que seja a lista de itens, estou chamando
isso de inventário. Agora, o que eu posso fazer é que o
inventário é uma lista, então isso é iterável. Então, posso obter um
iterador com isso. Para que eu possa escrever o inventário. Iterator é igual ao
inventário dot dr hitter. Isso vai me dar o iterador e então
eu posso imprimir, por exemplo ponto do iterador de
inventário
e, em seguida, o Dahmer. Então, isso está usando o protocolo
iterador. Se eu executar isso, você
verá que ele imprimirá o primeiro elemento
nessa sequência iterativa. Se eu copiar essa linha, desse jeito
, ela ligará no máximo
duas vezes e
passará pelo segundo item. Isso é o que você vê aqui. Há uma maneira um pouco
mais simples de fazer isso, em vez de chamar
esses métodos idiotas, o que também podemos
fazer é o correto. É ela, que é uma função auxiliar que simplesmente chama o
método dominante para nós. Da mesma forma, também
podemos usar next, que chama o próximo
método dominante para nós. Assim. E agora obtemos exatamente
o mesmo resultado. Então, eu posso
continuar fazendo isso. Então, há seis itens aqui. Então, agora vou imprimir todos os itens dessa lista
específica. Então, o que acontece se
eu adicionar mais um? Bem, então isso vai
gerar um erro de interrupção da iteração. Então você vê que
chegamos aqui rastreando, há uma iteração de parada. Então é assim que um
iterador indica: Ei, não
há mais, eu posso te dar qualquer outra coisa. Então,
agora você pode, por exemplo criar um loop while
e, em seguida
, testar as instruções exceto e, em seguida capturar
essas iterações de interrupção, então, você sabe, você é idiota. Mas é claro que ninguém está
usando iteradores dessa maneira. Não é uma maneira muito
mais simples de fazer isso, simplesmente
usando um loop for. Então, deixe-me remover
todas essas coisas aqui. E então eu vou escrever
para o item no inventário. E então eu
vou simplesmente imprimir o item ponto e vírgula, na verdade. Lá entramos e simplesmente
imprimimos todos os itens. Então, o que o loop for faz
nos bastidores é usar o método iter e next, então
ele primeiro obtém um
iterador e depois chama next várias vezes até encontrar
o erro
de interrupção da iteração
e depois parar. É isso que o loop for
faz. Nada mais. O que é bom na função
iter que chama o método doador é
que você também pode fornecer um valor sentinela. E é basicamente um valor
que deve ser fornecido para indicar o final
do iterador. Isso é útil, por exemplo,
se você estiver lendo dados de um arquivo ou rede, basicamente um fluxo de
dados e quiser
saber se o final do
fluxo foi atingido. Então, eu tenho um exemplo aqui. Portanto, este é um arquivo chamado country que contém
alguns países. E então eu posso usar o valor sentinela para indicar qual é o
final do arquivo. Portanto, posso usar declarações de largura. Isso é um gerenciador de contexto. Falarei sobre isso
em uma aula posterior. E eu vou usar o Open
e depois vou abrir arquivo TXT de pontos de
países. E então eu estou fazendo a
fila em uma entrada divertida. E então ele
chamará a linha de leitura de pontos phi, que retorna alguns iteráveis. E o valor da sentinela
será a string vazia. E então eu vou
imprimir a linha. Então, quando fazemos isso, você vê que temos
todos esses países. E como o arquivo de texto
já contém novas linhas, posso simplesmente removê-las
da instrução print
indicando que o final
da instrução print
é a string vazia. E então vai
ficar um pouco melhor. Portanto, a string vazia
aqui é usada como um valor sentinela para indicar que estamos chegando
ao final do arquivo.
17. Iteradores 2/3: Como temos esses
iteráveis e iteradores, eles também nos permitem
introduzir alguma abstração. Então, aqui eu tenho outro exemplo. Então, aqui está uma classe de item de linha. É uma classe de dados congelada. Eu falei sobre isso no
início do curso. E cada item da linha tem
um preço e uma quantidade. Esse é o preço total, isso retorna um int, e isso é simplesmente o preço
multiplicado pela quantidade. E então f para a função imprime
os totais que obtêm itens. E vejo que
não estou passando uma lista de itens de linha e passando a ela um iterável de itens e princípios de
linha. A única coisa que eu faria seria
usar o for-loop para item
em itens e um príncipe, o preço total na
minha função principal, eu tenho uma lista de itens de linha. Veja isso aqui, e então
eu estou chamando os totais impressos. Então, quando eu executo
isso, ele simplesmente imprime todos os totais. Mas o bom
agora é que, os totais
impressos esperam
algo iterável, não
importa se é uma lista, uma tupla
ou qualquer outra coisa. Então, por exemplo, aqui, se minha lista de itens de
linha, posso substituí-la
por uma tupla, assim. Então, agora não é mais uma lista, mas os totais impressos não se importam. Desde que seja iterável, ele pode fazer seu trabalho. Portanto, os iteradores permitem que
você também introduza alguma abstração porque as impressões Altos não se importam com
a estrutura de dados. Só se importa que
tenha algo sobre
o qual possa repetir e
isso é tudo de que precisa.
18. Iteradores 3/3: Se você realmente quiser levar
os iteradores para o próximo nível, o que você também pode fazer
é usar as ferramentas do GitHub, que é um pacote, pacote
padrão
do Python que tem mecanismos
iteradores
muito poderosos. É uma espécie de
álgebra de iterações. Como você pode pegar, todos esses
iteradores os combinam de
várias maneiras para obter um comportamento
realmente complexo. Por exemplo, suponha que você queira primeiro filtrar elementos
de uma lista e depois multiplicar esses elementos
por outro valor de outra lista e, em seguida
, vinculá-los com outro conjunto de outra lista e, em seguida
, vinculá-los com outro conjunto
de valores.
Talvez você normalmente usasse uma combinação
de funções
e, para loops, para isso. Mas você também pode usar outras ferramentas para combinar
essas operações em uma grande operação
usando álgebra iteradora. Vou mostrar alguns
exemplos de como isso funciona. Então, para não usá-lo, as
ferramentas, é claro, teremos que
importá-lo. Isso vai acabar. E depois há algumas
coisas que você pode fazer. Parece muito básico,
mas há algumas coisas
realmente avançadas que você
pode fazer com iteradores. Então, vou começar de novo com minha lista de inventário porque isso será
útil para mostrar
o que você pode fazer com iteradores. Vou apenas remover
essas linhas aqui. Bem, vamos começar com
algo muito simples. Um
exemplo muito simples é count, que é uma função
da itertools que permite contar a
partir de um determinado número. Então, podemos usar um for-loop
porque é iterável, certo? Temos contagens e então podemos indicar, por exemplo, o ponto
de partida. Então, queremos começar a
contar até dez. E você pode até
indicar uma etapa, por exemplo, com etapas de cinco e imprimir I. Agora, se eu parasse aqui, isso basicamente
continuará indefinidamente. Então, se chi é igual a, digamos, 50, então vamos quebrar. Então, quando eu executo isso, é isso que você obtém. Então essa é a
função de contagem, counts Iterable. Não é um exemplo simples que se repita. Então, agora isso vai
se repetir 105 vezes. Vamos executar isso e então você verá que isso é exatamente
o que está acontecendo. Outra coisa que você
pode fazer é acumular, que calcula as somas parciais. Então, digamos que temos subtotais, que são, digamos, uma
lista de alguns números. Estou apenas digitando
coisas aleatórias aqui, assim. E então o que podemos
fazer é usar acumular. E vamos fornecer
a lista de subtotais. Mas você pode
realmente fornecê-lo, basicamente qualquer outro iterável qual você possa
usar outras
funções de It's Tools, para o qual você possa
usar outras
funções de It's Tools, e então
eu simplesmente vou
imprimir I e isso
não precisamos neste caso. Então, o que vamos
fazer agora é examinar essa lista de subtotais e
sempre calcular a soma
parcial, e assim por diante. Isso é o que obtemos como resultado. Mais coisas que você pode
fazer com ele se resolvem. Então, digamos que
temos cartas de baralho, que é uma lista de,
digamos, sequências de caracteres. Estou usando o
co-piloto do GitHub aqui para gerar isso para mim, para que
eu não tenha realmente digitado isso. Mas agora o que você pode fazer
é usar permutações. Funcionamento de permutações. São as ferramentas de todas essas cartas
de baralho. E digamos que queremos ter
todas as permutações de, isso é tudo para jogar cartas. E então vamos imprimi-los. Como as fermentações estão
novamente em seu ribossomo, pode usar um loop for para
iterar sobre isso. Eles irão. E quando executamos isso, bem, isso é o que obtemos. Agora temos todas as combinações
possíveis de
quatro cartas de baralho diferentes. E vamos tornar isso um
pouco mais simples para que seja um pouco mais fácil
ver o que está acontecendo. Então, vou
criar um a, B e C.
Então, digamos que queremos ter
todas as combinações de dois. E então isso é
o que obtemos, certo? Então você vê que a ordem
realmente importa aqui. Então AB é outra coisa do que ba, se a ordem não importa, você não usa permutações, mas usa combinações, então é isso que obtemos lá. Obviamente, há
muito menos combinações desses três
caracteres, AB, AC e BC. Em vez de um
loop for para imprimir cada valor, também
podemos usar outra coisa. Podemos usar o inicializador de lista
para transformar os resultados
dessas combinações iteráveis em uma lista que podemos
simplesmente imprimir e é isso que obtemos. Há mais coisas que
você pode fazer com ferramentas,
por exemplo, você pode usar uma corrente. Então, se mudarmos os valores com, digamos, um analista d ,
e, f,
obteremos uma única lista
que é uma cadeia dessas duas listas separadas. E, claro, você pode passar
qualquer tipo de iterável aqui. Outra coisa que também é
útil é o filtro false. Assim, você pode imprimir,
digamos, uma lista de It's a
tool start, filter false. E eu quero
filtrar todos os itens com peso inferior a 1 kg. Então, vou usar uma função
lambda aqui x dot wait to write that right is less to write
that right is less than one. E, claro, vou passar meu inventário
para ele. E então deve
haver duas colunas aqui. Acho que não, não
estou perdendo nenhum parêntese. Então, quando eu executo isso, é isso que obtemos. Então, vemos que só
temos o laptop, o livro e a câmera, que pesam um quilo ou mais. Por último, quero
mostrar um mapa estelar, que é outra função da itertools
que permite pegar uma lista ou um
iterável de tuplas, neste caso de vários valores. E então, para cada valor você pode aplicar algum
tipo de operação. Então, aqui estou fazendo
uma multiplicação. Então, eu tenho uma lista de tuplas aqui. Então, o que isso
vai fazer é criar uma nova lista de duas vezes 68 vezes
4,5 vezes três. E esse é o resultado
que você vê aqui. Então esse é o mapa estelar. E agora podemos imaginar que com elas em duas funções, você pode realmente combinar
as coisas de várias maneiras. Portanto, você pode usar o filtro false para criar um novo iterável com um subconjunto dos
elementos e da lista. E se você usar acumulações para calcular o
peso total ou algo assim, poderá usar o mapa estelar
para calcular tempos de espera, quantidade, etc., etc. Você pode usar todas essas
combinações para criar uma espécie de álgebra que produza
um comportamento complexo. E essa é uma maneira diferente de
fazer isso do que, digamos, usando for-loops ou dividindo coisas e
funções e coisas assim. E você também pode,
é claro, criar seus próprios iteradores personalizados que fazem algo
específico para o que você precisa para combiná-los
perfeitamente com itertools porque tudo segue
o protocolo iterável. Então, aqui está um pequeno
exercício para você. Dê uma olhada em alguns dos seus códigos
anteriores e veja se você consegue encontrar alguns
exemplos em que você poderia, em vez de usar um
loop for para percorrer a lista, usar as ferramentas para realizar
essa operação específica. É muito divertido fazer anúncios que
às vezes podem levar a um código
muito mais curto. Mas tenha cuidado,
porque se você usar uma
combinação realmente complexa de ferramentas, também será
muito difícil de entender. Portanto, você precisa garantir que
seu código ainda esteja legível. E, claro, você
ainda pode
dividir bem as coisas em
diferentes funções, mesmo se estiver combinando
diferentes funções de ferramentas. Na próxima lição,
abordarei os iteradores
preguiçosos, também
chamados de geradores.
19. Geradores 1/4: Nesta lição,
vou falar sobre geradores e há algo especial
em Python dados criam o
chamado iterador preguiçoso e eles foram introduzidos
e o pepper 255. Então, o que realmente significa um
iterador preguiçoso? Bem, é quase o mesmo um iterador normal, exceto que ele só cria os valores
quando você solicita o valor. Então, em vez de ter, digamos que, se você tiver
uma lista com valores, defina a lista antes de poder realmente
iterá-la. Então você tem que criar
a lista completa. Com o iterador preguiçoso. Você só cria esses itens
à medida que os examina. Isso significa que o iterador preguiçoso não armazena seu
conceito na memória. Ele cria o conteúdo em tempo real quando você
o solicita. E isso significa que,
em alguns casos, essa é uma solução
mais adequada do que
computar tudo antemão antes de começar de
antemão antes de começar a iterar. Ela
também pode ser considerada uma maneira simples de criar iteradores,
porque basicamente gera uma classe com os métodos iter e next
dunder que você pode repetir, em vez de ter que escrever todas
essas coisas sozinho. E como você faz
isso? Bem, você simplesmente escreve uma função, assim como escreveria
uma função regular, exceto que os moldes usam uma
declaração de retorno para retornar um valor, mas você usa yield, tanto return quanto yields
retornam algum tipo de valor, exceto que return realmente
encerra a função. Rendimentos. Ele armazena o estado
da função como
está atualmente e retorna o valor. E então, na próxima vez
que você chamar a função, ela simplesmente recuperará esse estado novamente e continuamente de
onde parou.
20. Geradores 2/4: Aqui eu tenho um exemplo muito
simples de uma função geradora. Então você também vê que o tipo de retorno é um gerador e sobre
o que essas coisas fazem, falarei em um minuto. Mas isso é muito simples. Temos uma string e produzimos
o valor dessa string. Então essa é a primeira
vez que chamamos isso. Mas então adicionamos um ponto de
exclamação e o emitimos novamente. Isso cria um
iterador que você pode chamar duas vezes antes que a corrida
interrompa a iteração. E então, na função principal, estou usando for-loop para passar
pelo iterador que esse
gerador simples nos fornece. E então eu estou
imprimindo a corda. Então, aqui você vê qual é o
resultado dessa operação. Portanto, você usa isso como
um iterador normal , exceto que esse código só é executado quando você chama o
próximo método pela segunda vez. Então, o que é
esse tipo exatamente? Bem, isso degenera
o tipo que você pode importar da digitação, e isso nos diz qual é
o valor do rendimento. Então você pode ver que
na verdade é uma string. E o que isso significa quando você realmente tem um mecanismo de
envio de informações para
gerar uma função, e então essa
informação é usada novamente na próxima rodada. Aqui está um exemplo da documentação
do Python que mostra como isso funciona. É um pouco avançado, mas embora este seja um curso de
Python de próximo nível, por que não? Mas basicamente o que
acontece aqui é que
armazenamos o resultado
da expressão yield, que é o que enviamos
para essa função. E isso é do tipo float. E então, enquanto obtemos
um valor usando o centro, retornaremos a versão
arredondada disso, que é um número inteiro, e depois a versão final, que é o valor de retorno. Então, você pode realmente combinar
rendimento e retorno em um gerador e é
aí que a função termina e ela
retornará. Ok? Então, quando eu executo isso, você
pode ver que eu tenho aqui uma função principal onde
eu chamo isso do que eu faço a seguir. E então eu envio
esses valores para ele. E então você pode ver que ele passa por essas
coisas e termina com, ok, é basicamente
assim que funciona. Então, outro caso
de uso real para isso. Bem, não muitos, para ser honesto, talvez
você tenha um, mas nunca encontrei uma situação em que eu
precisasse usar isso. Também tenha cuidado ao
combinar rendimentos e retornos. Dessa forma, acho que
complica o dourado, torna mais difícil entender
o que realmente está acontecendo.
21. Geradores 3/4: A próxima coisa que
quero mostrar são as chamadas
expressões geradoras. E isso também é muito poderoso. Isso permite que você crie uma
expressão que seja, na verdade um gerador e especifique o cálculo imediatamente
na expressão. É um pouco como funções
lambda, função
anônima,
mas depois são geradores anônimos. Você vê um exemplo aqui. Eu tenho um gerador de energia em CC. Quando eu passo o mouse sobre esse valor, é, na verdade,
um gerador, mas uma expressão divertida
aqui entre parênteses, essa é a expressão geradora. E a expressão é dois
elevado a I para
I na faixa de dez. Portanto, é um gerador finito. E então eu estou usando um for-loop para iterar sobre esse
gerador de potências e, em seguida, imprimir os geradores de valor,
assim como ints ou floats ou funções são objetos que você pode passar para outras
funções, por exemplo, a função sum aceita um gerador e, portanto, também
uma expressão geradora. Assim, você pode criar
facilmente a soma de todas essas coisas simplesmente passando-a para
a função sum. Então, quando eu executo isso, é
isso que obtemos. A soma é 1023. As expressões geradoras
também são muito poderosas porque permitem fazer
cálculos como esse, mas só as fazem quando você
realmente precisa avaliar. Então, isso é diferente,
digamos, de uma compreensão de lista. Se eu mudasse esses
parâmetros por colchetes
, teríamos uma compreensão da
lista. Você também pode repetir isso, mas a
compreensão da lista
já é calculada quando você a define
como isso, o gerador. Então, isso é feito preguiçosamente, o que é muito útil se você quiser economizar algum tempo
de computação.
22. Geradores 4/4: Outra coisa interessante
sobre os geradores é que eles realmente se integram
bem com o código simultâneo. Tudo isso é uma das lições
anteriores que eu falei sobre async, IO e código simultâneo. Bem, na verdade, você também pode ter geradores de
concorrência assíncronos. Aqui está um exemplo. Eu
tenho uma função aqui. É uma
função assíncrona que recebe um nome de Pokémon aleatório. Portanto, é baseado no
exemplo Async IO que usei anteriormente. E isso simplesmente retorna
o nome do Pokémon. E então eu tenho aqui uma função
geradora que, depois do total,
é chamada de próximo
Pokémon. Então, eu passo uma volta
onde estou
analisando o total e toda vez que estou usando um peso para obter o nome aleatório do Pokémon e
, em seguida, estou produzindo esse nome. Você também pode pedalar Yield Away. Portanto, você não
precisa variar aqui. Mas, como você pode ver, ele se integra muito bem
com o código assíncrono. Você simplesmente escreve async na
frente dele e pode usar um peso em seu gerador, e então ele será
simultâneo. Mas o tipo é diferente. Há um async que gera
um tipo como você vê aqui. Mas, em princípio, a ideia é que
funcione da mesma forma. E então, se você olhar
na função principal, que também é síncrona, temos um loop
for assíncrono que passa
pelo próximo gerador de Pokémon e recebe os
próximos nomes de Pokémon. E você pode usar geradores
assíncronos de maneiras diferentes
e Python, por exemplo, aqui eu tenho uma
compreensão de lista que usa o próximo gerador de Pokémon
que é assíncrono. E então eu estou usando uma
compreensão de lista assíncrona para obter todos esses dados. Então, quando eu executar isso, você
verá que agora ele passa por essas chamadas de API e recebe
todos esses nomes de Pokémon. Isso não reúne
todas as respostas porque é um
gerador, então é preguiçoso. Ele só executará o
comando quando você o solicitar. É por isso que os vemos
aparecendo um por um. Mas isso significa
que, se você
precisar de apenas mais cinco nomes chame isso
apenas cinco
vezes, são apenas cinco solicitações de API. Em vez de fazer todas
as solicitações de API e, em seguida, pegar
apenas cinco valores. Essa é uma diferença entre
as dicas do tipo gerador e
a geração assíncrona de um tipo. E, na verdade, tive que
corrigir isso
há um minuto porque estava
errado porque a assíncrona gerada não
tem o tipo para o valor de retorno porque isso não é possível
com o assíncrono, só
temos rendimentos. É por isso que ele tem apenas dois
argumentos em vez de três. Mas, novamente, isso
não é algo que você normalmente
usaria
muito em seu código. Então, por que isso é útil? Bem, existe uma maneira fácil de
criar iteradores sem precisar criar uma classe com métodos
iter e next dunder. Obviamente, eles também
se integram bem com crianças ou ferramentas
mencionadas na lição anterior, economizam memória e
evitam computação desnecessária. Se você não solicitar o valor, ele não está sendo computado. E, em geral, é uma ótima
maneira de representar, por exemplo,
fluxos de dados em uma rede,
onde toda vez que por exemplo,
fluxos de dados em uma rede, você obtém o
próximo item da rede, algumas coisas a serem
observadas é que
os geradores podem resultar em
uma os geradores podem resultar camada mais difícil de
entender, especialmente se você começar a
combinar declarações de rendimento e retorno em
um único gerador. Em segundo lugar, como o
código é executado somente mediante solicitação, é
possível que você
encontre um erro somente depois tempo, o que pode levar a algum
tempo, o que pode levar a uma
caixa inesperada e mais tarde, então você precisa
se certificar de que está realmente testando corretamente. Portanto, no geral, os geradores
são uma ferramenta muito boa, podem não usá-los o tempo todo, mas em alguns casos
são muito úteis. Agora, vamos para a lição
final sobre gerenciadores de
contextos.
23. Gestores de contexto 1/4: O recurso final do Python
que eu quero abordar neste curso é o chamado gerenciador de
contexto. E isso se baseia em outros recursos sobre os quais
falei
nas lições anteriores, como
iteradores e geradores. E também é compatível
com código assíncrono Como mostrarei em
um minuto, contextos gerentes são muito
úteis
porque permitem que você
controle o que acontece quando você cria ou destrói recursos. E você pode garantir que, por exemplo se houver uma exceção ou se houver algum
outro motivo pelo qual o programa tenha que parar de
fazer o que está fazendo
, você pode usar um
gerenciador de contexto para controlar o que acontece
a fim de liberar um recurso. E isso é útil, por exemplo, se você precisar fechar um arquivo, se precisar fechar a conexão com
o banco de dados pois precisa deixar
outra superfície observar que seu aplicativo está interrompendo uma tarefa e você quer
ter certeza de que isso sempre acontece. É aí que os
gerenciadores de contextos são realmente úteis. É em uma das
lições anteriores que mostrei como
abrir um arquivo usando
a declaração de largura. Na verdade, esse é um exemplo
de gerenciador de contexto. E a razão pela qual usamos uma
declaração de largura é que
ela permite que o arquivo do
Gerenciador de
Contexto, o Gerenciador de Contexto, se feche adequadamente após você terminar de
ler o conteúdo. E há dois métodos
Dahmer, novamente, que são importantes. Existem os métodos dominantes de entrada e
saída. Enter é onde você coloca o que precisa
acontecer quando você cria um recurso e sai sempre que precisar
destruir o recurso.
24. Gestores de contexto 2/4: Aqui você vê outro exemplo
do uso de um gerenciador de contexto. Estou usando o SQL lite three
aqui, que se conecta a um banco de dados que é
apenas um arquivo local. E então eu estou selecionando itens
de uma tabela de blocos. E você vê que aqui simplesmente
criamos uma conexão com conexões de pontos
sqlite, mas você pode realmente usar
isso como um gerenciador de contexto. Quando executo esse código
, é isso que vemos. Então, veja, basicamente temos uma lista
de blogs. Mas o fato é que precisamos ter certeza de que estamos sempre fechando a conexão, certo? Então é isso que está acontecendo aqui. É por isso que isso não é
um bloqueio de tentativa final. Então, se eu, por exemplo, cometer um erro de digitação aqui, então blogs isso não existe. Então, vamos receber algum
tipo de erro que não é tabelado. Isso ainda garante que a conexão esteja fechada
corretamente. Agora, se você quiser evitar ter escrever isso, tente finalmente, o que também pode fazer
é usar o sqlite dot connect como gerenciador de contexto. Então, simplesmente
escrevemos width sqlite dot connect como coleção. E então isso,
é claro, eu tenho que
recuar e deve
haver uma coluna aqui. O que posso fazer agora é
fazer a mesma coisa, imprimir os blogs, mas depois
posso introduzir um erro. Mas a única coisa
é que agora você pode ver isso no registro é que na verdade, isso não
fecha a conexão corretamente. Isso é usado principalmente para
reverter transações no banco de dados e
coisas assim. Então, o que é bom sobre o Python
é que podemos realmente criar nossos próprios gerenciadores
de contextos. Só precisamos fornecer
uma classe que tenha
um método
dunder de entrada e saída e seguida, usá-la em vez disso. Então você vê um exemplo disso. Eu criei uma classe sqlite. Estou
fornecendo o nome do arquivo, então isso é aplicação de TB. E estou armazenando uma
nova conexão para conectar ao banco de dados
no método enter, então
pego o cursor
e depois a saída. Eu não confirmo as alterações e depois fecho a conexão. Então, em nossa função principal,
temos com o SQL light. Então, agora estou usando esse gerenciador de
contexto aqui, e isso vai me dar
o cursor e então eu posso executar minha consulta e
retornar o resultado. Então, quando eu executar isso,
obteremos novamente exatamente os
mesmos resultados, certo? Vamos imprimir esses blogs, mas depois ele chama de resposta e
chama de saída quando estiver concluído. Portanto, temos certeza de que a conexão está sempre fechada porque agora, por exemplo deixe-me apresentar aqui
novamente algum erro. Se eu executar isso novamente, você verá que, na verdade, ele ainda
chamou enter e exit. O gerenciador de contexto fez o trabalho de
fechar corretamente a conexão, mesmo se tivermos um erro.
25. Gestores de contexto 3/4: Agora, em vez dessa classe
com métodos de entrada e saída, o que você também pode fazer é usar um
decorador que, na verdade se baseia na geração
de uma função mencionada
na lição anterior. Então isso é o que parece. Estou importando o decorador
do Context
Manager de contextos ativos e divertidos aqui
para indicar que essa função será um gerenciador de contexto. E então você vê que estou usando
uma estrutura geradora com rendimento para realmente
definir o que deve acontecer nas partes de resposta
e saída. Então, o que funciona é
que tudo antes da produção são
as partes principais. Tudo depois disso
é a parte de saída. Então, isso é basicamente
o que você vê aqui. E isso tem exatamente o mesmo
efeito que usar uma classe. Ou se eu executar isso, você
vê que estamos fechando uma conexão aqui e uma conexão
criando e
imprimindo os blogs. Se eu remover aqui novamente, vamos remover outro
personagem só por diversão. E então vamos executar isso novamente. Você vê que ainda recebemos esse erro, mas agora ele ainda fecha a
conexão corretamente porque estamos usando um
gerenciador de contexto, decorador.
26. Gestores de contexto 4/4: A última coisa que
quero mostrar é que os gerenciadores de
contexto também
suportam a
sintaxe async and await da integração bem com código
simultâneo, assim como os geradores,
se ela quiser
conceder um gerenciador de
contexto assíncrono, usaram o decorador assíncrono do Context
Manager. Agora, há alguns
problemas de digitação aqui porque esse é um exemplo mais aproximado. Mas a ideia é que você esteja
usando nesse caso assíncrono em vez de
um gerador síncrono regular. Portanto, os gerentes de contextos também trabalham
nesse sentido. Então, por exemplo, aqui, digamos que você tenha uma camada de banco de dados assíncrona. Você pode usar a
sintaxe await async para chegar aqui, por exemplo , conexão de
banco de dados, nós
usamos a conexão e liberação também é assíncrona e colocamos async na frente dela. E então você pode usar isso
no resto do seu código, como estou fazendo aqui, por exemplo, estou recebendo todos os usuários. Sql light em si
não é assíncrono, mas há um pacote
chamado sclerites que é assíncrono que você pode usar para acessar um
banco de dados SQL light de forma assíncrona. Você vê um exemplo simples
de como isso funciona. Então eu tenho, em vez de
sqlite dot connect, eu faço, eu todos sqlite dot connect. Mas isso é assíncrono, mas ainda usa as declarações de
largura. E você faz a mesma
coisa para obter um cursor para buscar todos os
dados da tabela. Então, quando você usa isso, bem, os lugares mais importantes onde você precisa ter
certeza de que está fechando ou limpando
adequadamente sua
bagunça, mesmo que
algo dê errado. E esse é particularmente
o caso dos
bancos de dados que
monitoramos as conexões. Ou você quer ter
certeza de fechar essa conexão. Ou talvez você precise deixar uma API dizer que você
concluiu algum tipo de processo.
Você quer ter certeza de
que, mesmo que ocorra uma exceção, essa
chamada final ainda ocorra. E é aí que os
gerenciadores de contexto são realmente úteis. E, finalmente, do ponto de vista do
design de software, é muito bom que
você possa
agrupar recursos de criação e destruição
em um único lugar.
27. Resumo: Muito obrigado por
seguir este curso. Espero que tenha lhe dado um pouco
de reflexão e algumas ideias sobre como melhorar
seu código Python existente. Eu me diverti muito
gravando esse curso, mas é claro que há muitas
outras coisas nas quais poderíamos mergulhar. Um pouco mais sobre Python, design de
software em particular, como mencionei
no início, confira meu canal no YouTube, youtube.com Slash ion codes. Se você tiver alguma dúvida ou sugestão, basta
escrever um comentário aqui. Eu adoraria ouvir
de você. Obrigado por assistir e cuidar.