Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/isaacalves7/python-studies

🐍 It's a repository of Python programming language and his content.
https://github.com/isaacalves7/python-studies

anaconda bottle cherrypy cpython django fastapi flask jupyter kivy lamp pip pypi pypy pyqt5 pyscaffold python3 r-language sqlite-database zeep

Last synced: 21 days ago
JSON representation

🐍 It's a repository of Python programming language and his content.

Awesome Lists containing this project

README

        

![JS](https://img.shields.io/badge/Django-2ba977?style=for-the-badge&logo=Django&logoColor=ffffff) ![JS](https://img.shields.io/badge/FastAPI-2ba969?style=for-the-badge&logo=FastAPI&logoColor=ffffff) ![TS](https://img.shields.io/badge/Flask-000000?style=for-the-badge&logo=Flask&logoColor=ffffff) ![TS](https://img.shields.io/badge/NumPy-blue?style=for-the-badge&logo=NumPy&logoColor=ffffff)
![TS](https://img.shields.io/badge/Pandas-magenta?style=for-the-badge&logo=Pandas&logoColor=white) ![TS](https://img.shields.io/badge/Numba-blue?style=for-the-badge&logo=Numba&logoColor=ffffff) ![TS](https://img.shields.io/badge/PySpark-E25A1C?style=for-the-badge&logo=Apache-Spark&logoColor=ffffff) ![TS](https://img.shields.io/badge/PyPy-193440?style=for-the-badge&logo=PyPy&logoColor=ffffff)

# It's a repository of Python language 🐍

> 🐍 **Preparação**: Para este conteúdo, o aluno deverá dispor de um computador com acesso à internet, um web browser com suporte a HTML 5 (Google Chrome, Mozilla Firefox, Microsoft Edge, Safari, Opera etc.), um editor de texto ou IDE (VSCode etc.) e o software Python3, com a versão mais recente, instalado na sua máquina local.

- https://exercism.org/tracks/python
- https://app.datacamp.com/learn

# 🐍 The History of Python language

Dentre as diversas linguagens de programação que existem, **Python** é considerada uma das principais. Por sua simplicidade de aprendizado, ela tem sido utilizada em diversos cursos universitários como a primeira linguagem com que os alunos têm contato ao programar. Atualmente, conta com ampla participação da comunidade, além de ter seu desenvolvimento aberto e gerenciado pela organização sem fins lucrativos Python Software Foundation.

Recentemente, a IEEE Computer Society classificou-a como a linguagem mais indicada para aprender em 2020. Isso se deve à sua eficiência no desenvolvimento de machine learning, inteligência artificial, ciência, gestão e análise de dados.

A linguagem Python surgiu em 1989, criado por Guido Van Rossum, nascido em 1956 em Haarlem, Holanda. O desenvolvimento da linguagem foi como um hobby onde a ideia era dar continuidade a linguagem ABC que era desenvolvida no Centro de Pesquisa Holandês (CWI) chamado Centrum Voor Wiskunde en Informatica, em Amsterdã, na Holanda no ano de 1980.

As circunstâncias em que o Python foi criado são um pouco confusas. De acordo com Guido van Rossum: Em Dezembro de 1989, estava à procura de um projeto de programação de "hobby" que me mantivesse ocupado durante a semana por volta do Natal. O meu escritório (...) estaria fechado, mas eu tinha um computador em casa, e não tinha muito mais nas mãos. Decidi escrever um intérprete para a nova linguagem de escrita em que tinha pensado ultimamente: um descendente do ABC que apelaria aos hackers Unix/C. Escolhi Python como título de trabalho para o projeto, estando de humor ligeiramente irreverente (e sendo um grande fã do Monty Python's Flying Circus). - Guido van Rossum

A origem do nome foi inspirado na comédia televisiva inglesa da BBC chamada "Monty Python and the Flying Circus", na década de 1970. A logo de uma serpente da espécie python foi criado pela O'Reilly Media, uma empresa de mídia conhecida por seus livros técnicos na área de Ciência e Tecnologia. A O'Reilly Media usou a imagem de uma serpente em sua capa do livro "Programming Python" de Mark Lutz, que foi lançado em 1996. A capa apresentava uma serpente enroscada, e essa imagem acabou sendo adotada como um símbolo informal para o Python. Então, embora a inspiração para o nome "Python" venha do programa de televisão "Monty Python's Flying Circus", o logo específico da serpente foi popularizado pela O'Reilly Media por meio de sua capa de livro. A serpente passou a ser amplamente reconhecida como o símbolo do Python na comunidade de desenvolvimento de software e após isso foi aderida pelo fundador.

Uma das características espantosas de Python é o fato de ser realmente o trabalho de uma pessoa. Normalmente, novas linguagens de programação são desenvolvidas e publicadas por grandes empresas que empregam muitos profissionais, e devido às regras de direitos de autor, é muito difícil nomear qualquer uma das pessoas envolvidas no projeto. O Python é uma exceção e, portanto, é claro que Guido van Rossum não desenvolveu e evoluiu ele próprio todos os componentes de Python.

A rapidez com que o Python se espalhou pelo mundo é o resultado do trabalho contínuo de milhares (muitas vezes anônimos) de programadores, testadores, utilizadores (muitos deles não são especialistas em IT) e entusiastas, mas deve dizer-se que a primeira ideia (a semente da qual o Python brotou) chegou a uma cabeça - a de Guido.

Em 1999, Guido van Rossum definiu os seus objetivos para o Python, cujo foi influenciada por ABC que era uma linguagem pensada para iniciantes devido a sua facilidade de aprendizado e utilização, são eles:

- Uma **linguagem fácil e intuitiva**, tão poderosa como a dos principais concorrentes;
- Código aberto, **open source**, para que todos possam contribuir;
- Código que seja tão **compreensível e inteligível** como o idioma inglês simples;
- **Adequado a tarefas diárias**, e produtiva, permitindo tempos de desenvolvimento curtos.

> Cerca de 20 anos mais tarde, é evidente que todas estas intenções foram cumpridas. Algumas fontes dizem que o Python é a linguagem de programação mais popular no mundo, enquanto outras afirmam que é a terceira ou a quinta.

Seja como for, continua a ocupar uma posição elevada no top dez do PYPL PopularitY of Programming Language e no TIOBE Programming Community Index.

O Python não é uma linguagem jovem. É **madura** e **confiável**. Não é uma *one-hit wonder*. É uma estrela brilhante no firmamento da programação, e o tempo gasto a aprender Python é um investimento muito bom.

Como é que os programadores, jovens e velhos, experientes e novatos, querem utilizá-lo? Como aconteceu que grandes empresas adotassem o Python e implementassem os seus principais produtos utilizando-o?

Há muitas razões - já enumerámos algumas delas, mas vamos enumerá-las novamente de uma forma mais prática:

- **é fácil de aprender** - o tempo necessário para aprender Python é menor do que para muitas outras linguagens; isto significa que é possível iniciar a programação em si mais rapidamente;
- **é fácil de ensinar** - a carga de trabalho de ensino é menor do que a necessária para outras linguagens; isto significa que o professor pode colocar mais ênfase em técnicas de programação gerais (independentes da linguagem), não desperdiçando energia em truques exóticos, estranhas exceções e regras incompreensíveis;
- é **fácil de usar** para escrever novo software - é muitas vezes possível escrever código mais rapidamente quando se usa Python;
- é **fácil de compreender** - é também frequentemente mais fácil e rápido de compreender o código de outra pessoa se for escrito em Python;
- é **fácil de obter, instalar e implementar** - o Python é gratuito, aberto e multiplataforma; nem todas as linguagens se podem gabar disso.

> O Python é uma linguagem de programação com extenso uso, com uma grande comunidade de contribuintes e com extensa disponibilidade de artigos, informações e bibliotecas.

É claro que o Python também tem os seus inconvenientes (ninguém é perfeito hehe):

- **não tem um super-poder da velocidade** - o Python não oferece um desempenho excecional;
- em alguns casos pode ser resistente a algumas técnicas de teste mais simples - isto pode significar que **depurar (debuggar) o código Python pode ser mais difícil do que com outras linguagens**; felizmente, cometer erros é sempre mais difícil em Python.

Apesar da popularidade crescente de Python, ainda existem alguns nichos onde o Python está ausente, ou raramente é visto:

- **programação de baixo nível** (por vezes chamada programação "close to metal"): se quiser implementar um condutor ou motor gráfico extremamente eficaz, não utilizaria Python;
- **aplicações para dispositivos móveis**: existe pouca área de atuação para o Python nessa área, fortalecendo assim algumas das linguagens concorrentes.

Deve também ser afirmado que o Python não é a única solução do seu gênero disponível no mercado do TI. Tem muitos seguidores, mas há muitos que preferem outras linguagens e nem sequer consideram o Python para os seus projetos.

No início dos anos 1990 e desde então tem aumentado sua participação no mundo da programação. Permite uma programação fácil e clara para escalas pequenas e grandes, além de enfatizar a legibilidade eficiente do código, notadamente usando espaços em branco significativos.

Dentre as diversas linguagens de programação que existem, **Python** é considerada uma das principais. Por sua simplicidade de aprendizado, ela tem sido utilizada em diversos cursos universitários como a primeira linguagem com que os alunos têm contato ao programar. Atualmente, conta com ampla participação da comunidade, além de ter seu desenvolvimento aberto e gerenciado pela organização sem fins lucrativos Python Software Foundation.

Recentemente, a _IEEE Computer Society_ classificou-a como a linguagem mais indicada para aprender em 2020. Isso se deve à sua eficiência no desenvolvimento de **machine learning**, **inteligência artificial**, **ciência**, **gestão** e **análise de dados**.

**Python** é uma linguagem de programação de alto nível, que permite ao programador utilizar instruções de forma intuitiva, tornando seu aprendizado mais simples do que o aprendizado de uma linguagem de baixo nível.

Nas linguagens de baixo nível, o programador precisa se expressar de forma muito mais próxima do que o dispositivo “entende”, levando naturalmente a um distanciamento da linguagem utilizada para comunicação entre duas pessoas.

A classificação das linguagens em paradigmas permite que entendamos qual é o melhor deles para solucionar determinado problema e, a partir daí, escolher a linguagem de programação (pertencente a esse paradigma) mais adequada, conforme características e especificidades do contexto em que se aplica o problema.

A linguagem Python foi escolhida como instrumento para o desenvolvimento desta disciplina, pois além de ser multiparadigma (possibilita escrever programas em diferentes paradigmas) e de uso geral, vem se destacando e sendo cada vez mais utilizada entre os novos desenvolvedores por vários motivos:

- Facilidade de aprendizado;
- Boa legibilidade de código;
- Boa facilidade de escrita;
- Produtividade e confiabilidade.
- Possui, ainda, comunidade de desenvolvedores crescente e vasta biblioteca, repleta de funções, aplicada a diversas áreas da ciência, assim como o crescente números de frameworks desenvolvidos para a linguagem.

O Python é uma linguagem de programação dinâmica de alta produtividade amplamente usada em aplicativos de ciência, engenharia e análise de dados.

Há uma série de fatores que influenciam a popularidade do Python, incluindo sua sintaxe limpa e expressiva, estruturas de dados padrão, biblioteca padrão abrangente, "baterias incluídas", documentação excelente, amplo ecossistema de bibliotecas e ferramentas, disponibilidade de suporte profissional e uma grande e aberta comunidade.

> Talvez o mais importante, porém, seja a alta produtividade que uma linguagem interpretada e digitada dinamicamente como o Python permite. Python é ágil e flexível, o que o torna uma ótima linguagem para prototipagem rápida, mas também para construir sistemas completos.

A linguagem **Python** é uma linguagem de programação, com características interessantes:

- É **interpretada** e **compilada**, ou seja, o interpretador Python executa o código fonte diretamente, traduzindo cada trecho para instruções de máquina;
- É de **alto nível**, ou seja, o interpretador se vira com detalhes técnicos do computador. Assim, desenvolver um código mais simples do que em linguagem de baixo nível, nas quais o programador deve se preocupar com detalhes da máquina;
- É de propósito geral, ou seja, podemos usar Python para desenvolver programas em diversas áreas. Ao contrário de linguagens de domínio específico, que são especializados e atendem somente a uma aplicação específica;
- Tem **tipos dinâmicos**, ou seja, o interpretador faz a magia de descobrir o que é cada variável;
- É **multiparadigma**, apesar de suportar perfeitamente o paradigma de programação estruturada, Python também suporta programação orientada a objetos, tem características do paradigma funcional, com o amplo uso de bibliotecas, assim como permite recursividade e uso de funções anônimas.
- É **interativa**, permite que os usuários interajam com o interpretador Python diretamente para escrever os programas, utilizando o prompt interativo. Esse prompt fornece mensagens detalhadas para qualquer tipo de erro ou para qualquer comando específico em execução, suporta testes interativos e depuração de trechos de código.
- É **híbrida** quanto ao método de implementação. Python usa uma abordagem mista para explorar as vantagens do interpretador e do compilador. Assim como Java, utiliza o conceito de máquina virtual (PVM - Python Virtual Machine), permitindo a geração de um código intermediário, mais fácil de ser interpretado, mas que não é vinculado definitivamente a nenhum sistema operacional.
- É **portável**, tem a capacidade de rodar em uma grande variedade de plataformas de hardware com a mesma interface. Ele roda perfeitamente em quase todos os sistemas operacionais, como **Windows**, **Linux**, **UNIX**, e **macOS**, sem nenhuma alteração.
- É **extensível**, permite que os programadores adicionem ou criem módulos e pacotes de baixo nível / alto nível ao interpretador Python. Esses módulos e pacotes de ferramentas permitem que os desenvolvedores tenham possibilidades amplas de colaboração, contribuindo para a popularidade da linguagem.
- **Suporta bancos de dados**, por ser uma linguagem de programação de uso geral, Python suporta os principais sistemas de bancos de dados. Permite escrever código com integração com **MySQL**, **PostgreSQL**, **SQLite**, **ElephantSQL**, **MongoDB**, entre outros.
- **Suporta interface com usuário**, permite escrever código de tal maneira que uma interface do usuário para um aplicativo possa ser facilmente criada, importando bibliotecas como **Tkinter**, **Flexx**, **CEF Python**, **Dabo**, **Pyforms** ou **PyGUI wxPython**, **PyQT**, **Kivy**.
- Pode ser usado como **linguagem de script**. Permite fácil acesso a outros programas, podendo ser compilado para **bytecode** a fim de criar aplicativos grandes.
- Permite **desenvolvimento de aplicações Web**. Devido à escalabilidade já citada, Python oferece uma variedade de opções para o desenvolvimento de aplicativos Web. A biblioteca padrão do Python incorpora muitos protocolos para o desenvolvimento da web, como **HTML**, **XML**, **JSON**, **processamento de e-mail**, além de fornecer base para **FTP**, **IMAP** e outros **protocolos da Internet**.
- Permite criação de **aplicações comerciais**. É desenvolvido sob uma licença de código aberto aprovada pela **OSI**, tornando-o livremente utilizável e distribuível, mesmo para uso comercial.

> **Atenção**: Mas a maior força do Python também pode ser sua maior fraqueza: sua flexibilidade e sintaxe de alto nível sem tipo podem resultar em baixo desempenho para programas com uso intensivo de dados e computação. Por esse motivo, os programadores Python preocupados com a eficiência geralmente reescrevem seus loops mais internos em C e chamam as funções C compiladas do Python.

Existem vários projetos que visam tornar essa otimização mais fácil, como o **Cython**, mas geralmente exigem o aprendizado de uma nova sintaxe. Idealmente, os programadores Python gostariam de tornar seu código Python existente mais rápido sem usar outra linguagem de programação e, naturalmente, muitos gostariam de usar aceleradores para obter um desempenho ainda maior de seu código.

Resumindo as características do Python:

- Orientada a objetos com uma semântica dinâmica;
- Possui licença compatível com Software livre;
- Linguagem de altíssimo nível (VHLL);
- Tipagem dinâmica e forte;
- Aumenta a produtividade do desenvolvedor;
- A implementação padrão e oficial de referência do Python que é mantida pela PSF (Python Software Foundation) e é escrita em linguagem C, e por isso, é também conhecida como CPython;
- Multiplataforma e multiparadigma (POO, funcional e procedural);
- Batteries Included: é uma biblioteca padrão rica e versátil que está imediatamente disponível, sem que o usuário baixe pacotes separados. Isso dá à linguagem Python uma vantagem inicial em muitos projetos.;
- Organizada;
- Comunidade gigante e ativa;
- Curva de aprendizado baixa;
- Muitas Bibliotecas.

Por essas e várias outras características, o Python se torna uma linguagem simples, bela, legível e amigável. É uma linguagem muito utilizada por diversas empresas como Wikipédia, Microsoft, Google, Yahoo!, CERN, NASA, Facebook, AMAZON, Instagram, Spotify, Bitorrent Inc, Django e Dropbox.

![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=flat&logo=ruby&logoColor=white) ![Perl](https://img.shields.io/badge/Perl-39457E?style=flat&logo=Perl&logoColor=white)

O Python tem dois concorrentes diretos, com propriedades e predisposições comparáveis. Estes são:

- **Perl** - uma linguagem de scripting originalmente de autoria de Larry Wall;
- **Ruby** - uma linguagem de scripting originalmente escrita por Yukihiro Matsumoto.

A primeira é mais tradicional, mais conservadora do que Python, e assemelha-se a algumas das boas e antigas linguagens derivadas da clássica linguagem de programação C.

Em contraste, esta última é mais inovadora e mais cheia de ideias frescas do que Python. O próprio Python encontra-se algures entre estas duas criações.

A Internet está cheia de fóruns com infinitas discussões sobre a superioridade de um destes três sobre os outros, caso pretenda saber mais sobre cada um deles.

### Principais áreas de atuação com a linguagem Python
Vemo-lo todos os dias e em quase todo o lado. É utilizado extensivamente para implementar **serviços complexos da Internet** como motores de busca, armazenamento em nuvem e ferramentas, redes sociais, etc. Sempre que utiliza qualquer um destes serviços, está na realidade muito próximo de Python, embora não o conheça.

Muitas ferramentas em desenvolvimento são implementadas em Python. Cada vez mais aplicações de uso diário estão a ser escritas em Python. Muitos cientistas abandonaram ferramentas proprietárias dispendiosas e mudaram para o Python. Muitos testadores de projetos de TI começaram a utilizar o Python para realizar procedimentos de teste repetíveis. A lista é longa:

- IA - Inteligência Artificial
- Machine Learning
- Deep Learning
- IoT - Internet das Coisas
- Big Data
- Data Analysis
- Data Science
- Computação 3D
- Biotecnologia
- Bioinformática
- Web Development (Back-end)
- Cybersecurity
- Game Development
- Mobile Development
- Desktop Development
- DevSecOps
- QA - Quality Assurance
- Automação de Sistemas
- Cloud Computing
- Estudos científicos como: Engenharia, Geologia, Astronomia, Física, Química, Matemática e etc

Certificações em Python:

![3b74900cebc980b0fa8bcf4bb86c85488d6987c8](https://user-images.githubusercontent.com/61624336/194156459-aa30790d-bcb5-4966-af03-d2fb3acaa607.png)

- https://pythoninstitute.org/pcep
- https://pythoninstitute.org/pcap
- https://pythoninstitute.org/

## Há mais de um Python

Existem dois tipos principais de Python, chamados **Python 2** e **Python 3**. O Python 2 é uma versão mais antiga do Python original. Desde então o seu desenvolvimento tem sido intencionalmente parado, embora isso não signifique que não hajam atualizações. Pelo contrário, as atualizações são emitidas regularmente, mas não se destinam a modificar a linguagem de forma significativa. Preferem corrigir quaisquer bugs recém-descobertos e falhas de segurança. O caminho de desenvolvimento de Python 2 já chegou a um beco sem saída, mas o Python 2 em si ainda está muito vivo, presente principalmente em sistemas operacionais Linux e macOS.

Em 2008, é lançada a versão 3.0, que resolveu muitos problemas de design da linguagem e melhorou a performance. Algumas mudanças foram muito profundas dessa forma a versão 3.x não é retrocompatível.

O Python 3 é a versão mais recente (para ser mais preciso, a atual versão) da linguagem. Está a percorrer o seu próprio caminho de evolução, criando os seus próprios padrões e hábitos. Atualmente, estamos na versão 3.10.8 do Python.

Estas duas versões do Python não são compatíveis uma com a outra. Os **scripts** (Arquivos de texto que contém instruções que constituem um programa de Python) de Python 2 não serão executados num ambiente Python 3 e vice-versa, portanto, se quiser que o antigo código Python 2 seja executado por um interpretador Python 3, a única solução possível é reescrevê-lo, não do zero, claro, pois grandes partes do código podem permanecer intocadas, mas terá de rever todo o código para encontrar todas as incompatibilidades possíveis. Infelizmente, este processo não pode ser totalmente automatizado.

É demasiado difícil, demasiado demorado, demasiado caro e demasiado arriscado migrar uma velha aplicação Python 2 para uma nova plataforma. É possível que a reescrita do código lhe introduza novos bugs. É mais fácil e mais sensato deixar estes sistemas em paz e melhorar o intérprete existente, em vez de tentar trabalhar dentro do source code já em funcionamento.

O Python 3 não é apenas uma versão melhor do Python 2 - é uma linguagem completamente diferente, embora seja muito semelhante à sua predecessora. Quando se olha para eles à distância, parecem ser os mesmos, mas quando se olha de perto, no entanto, notam-se muitas diferenças.

Se estiver a modificar uma antiga solução Python existente, então é altamente provável que tenha sido codificada em Python 2. Esta é a razão pela qual o Python 2 ainda está a ser utilizado. Há demasiadas aplicações Python 2 existentes para o descartar completamente.

> **Nota**: Se vai iniciar um novo projeto Python, deve usar Python 3.

É importante lembrar que pode haver diferenças menores ou maiores entre as versões posteriores do Python 3 (por exemplo, Python 3.6 introduziu chaves de dicionário ordenadas por defeito sob a implementação do CPython) - a boa notícia, porém, é que todas as versões mais recentes de Python 3 são retrocompatíveis com as versões anteriores de Python 3. Sempre que for significativo e importante, tentaremos realçar essas diferenças.

> Todas as amostras de código que irá encontrar aqui foram testadas com Python 3.4, Python 3.6, Python 3.7, e Python 3.8.

## Python aka CPython

Além do Python 2 e Python 3, existe mais de uma versão de cada uma. Em primeiro lugar, existem os Pythons que são mantidos pelas pessoas reunidas em torno da PSF (Python Software Foundation), uma comunidade que visa desenvolver, melhorar, expandir e popularizar o Python e o seu ambiente. O presidente da PSF é o próprio Guido von Rossum, e por esta razão, estes Pythons são chamados de canônicos. São também considerados **Pythons de referência**, pois qualquer outra implementação da linguagem deve seguir todas as normas estabelecidas pelo PSF.

Guido van Rossum utilizou a **linguagem de programação C** para implementar a primeira versão da sua linguagem, e esta decisão ainda está em vigor. Todos os Pythons provenientes do PSF são escritos na linguagem C. Há muitas razões para esta abordagem e ela tem muitas consequências. Uma delas (provavelmente a mais importante) é que graças a ela, o Python pode ser facilmente portado e migrado para todas as plataformas com a capacidade de compilar e executar programas em linguagem C (praticamente todas as plataformas têm esta característica, o que abre muitas oportunidades de expansão para Python).

É por isso que a implementação da PSF é frequentemente referida como **CPython**. Este é o Python mais influente entre todos os Pythons do mundo.

O CPython é uma **implementação** da linguagem Python, um pacote com um compilador e um interpretador Python (Máquina Virtual Python - PVM), além de outras ferramentas para programar em Python.

## Cython

Outro membro da família Python é o **Cython** que é uma das várias soluções possíveis para a mais dolorosa das características de Python - **a falta de eficiência**. Grandes e complexos cálculos matemáticos podem ser facilmente codificados em Python (muito mais facilmente do que em C ou qualquer outra linguagem tradicional), mas a execução do código resultante pode ser extremamente demorada.

Como são conciliadas estas duas contradições? Uma solução é escrever as suas ideias matemáticas usando Python, e quando estiver absolutamente seguro de que o seu código está correto e produz resultados válidos, pode traduzi-lo para C. Certamente, o C correrá muito mais rápido do que Python puro.

É isto que o Cython pretende fazer - traduzir automaticamente o código Python (limpo e claro, mas não demasiado rápido) em código C (complicado e falador, mas ágil).

## Jython

Outra versão do Python é chamada **Jython**, o “J” é para “Java”. Imagine um Python escrito em Java em vez de C. Isto é útil, por exemplo, se desenvolver sistemas grandes e complexos escritos inteiramente em Java, e quiser acrescentar alguma flexibilidade Python a eles. O CPython tradicional pode ser difícil de integrar em tal ambiente, já que C e Java vivem em mundos completamente diferentes e não partilham muitas ideias comuns.

Jython pode comunicar com a infra-estrutura Java existente de forma mais eficaz. É por isso que alguns projetos o consideram utilizável e necessário.

> **Nota**: A atual implementação do Jython segue as normas do Python 2. Até ao momento, não há Jython em conformidade com Python 3.

> **Curiosidade**: Curioso saber que o código Python pode ser traduzido em Bytecode Java usando a implementação **Jython** para rodar aplicações Java e na JVM - Java Virtual Machine.

## PyPy e RPython

Dê uma vista de olhos ao logotipo acima. É um rébus. Consegue resolvê-lo? É um logótipo do **PyPy** - *um Python dentro de um Python*. Por outras palavras, representa um ambiente Python escrito em linguagem Python, chamado **RPython** (Restricted Python). Na verdade, é um subconjunto de Python.

O source code de PyPy não é executado na forma de interpretação, mas sim traduzido para a linguagem de programação C e depois executado separadamente.

Isto é útil porque se quiser testar qualquer nova funcionalidade que possa ser (mas não tem de ser) introduzida na implementação do Python convencional, é mais fácil verificá-la com o PyPy do que com o CPython. É por isto que o PyPy é antes uma ferramenta para pessoas que desenvolvem Python, do que para o resto dos utilizadores.

Isto não torna o PyPy menos importante ou menos sério do que o CPython, é claro.

Além disso, o PyPy é compatível com a linguagem do Python 3.

Existem muitos mais Pythons diferentes no mundo. Encontrá-los-á se procurar, vamos nos concentrar no CPython.

# 🏗️ Sistema de implementação do Python

**Python** usa um sistema híbrido, uma combinação de interpretador e tradutor (compilador). O **compilador** converte o código-fonte Python em um código intermediário, que roda numa máquina virtual, a **PVM** (Python Virtual Machine).

O Python é uma linguagem interpretada. Isto significa que herda todas as vantagens e desvantagens descritas. Naturalmente, acrescenta algumas das suas características únicas a ambos os conjuntos.
Se quiser programar em Python, precisará do intérprete Python. Não será capaz de executar o seu código sem ele. Felizmente, o Python é gratuito. Esta é uma das suas vantagens mais importantes.
Devido a razões históricas, as linguagens concebidas para serem utilizadas na forma de interpretação são muitas vezes chamadas linguagens de scripting, enquanto os source programs codificados que as utilizam são chamados scripts.

O **virtualenv** do Python é utilizado para isolar a versão do Python e das bibliotecas usadas em um determinado sistema. Caso você não utilize o virtualenv, todas as bibliotecas necessárias para seu sistema seriam instaladas no ambiente do sistema operacional. Existe outra forma de fazer isso com o **Pipenv**.

# 🐍 Python Programming
Vamos focar nos principais fundamentos para programar na linguagem python.

## UTILITÁRIOS E MÓDULOS
Apenas como exemplo, na área de Console clique no botão **Python Console**. No prompt interativo `>>>` que se abrirá, digite `x = 5` e pressione a tecla [ENTER] ou [RETURN] do seu teclado. Observe na figura 2 que, na área Árvore de exibição de variáveis, agora fica disponível a informação que a variável `x` é do tipo `int` e tem o valor `5`.

![figura02](https://user-images.githubusercontent.com/61624336/133014438-1af0799c-dc1a-4d2b-bfcb-bd1bbb544a0b.png)

Não se preocupe ainda com o conceito de variável, nem com o seu tipo. Veremos tudo isso com detalhes nos próximos módulos deste tema.

O utilitário **dir** apresenta todos os atributos e métodos disponíveis para determinado tipo de dado. No prompt interativo `>>>`, digite dir(x) e pressione a tecla [ENTER] ou [RETURN] do seu teclado.

No prompt interativo `>>>`, digite `dir(x)` e pressione a tecla [ENTER] ou [RETURN] do seu teclado.

![figura03](https://user-images.githubusercontent.com/61624336/133014701-4115d1d5-dac0-4260-81cf-6df3e3723127.png)

O utilitário `help` apresenta a documentação relativa a determinado tipo de dado. Caso você tenha alguma dúvida sobre o que é possível fazer com determinado tipo, os utilitários **dir** e **help** podem ser úteis.

## BLOCOS
Em **Python**, os **blocos** são definidos pela **indentação**. Diferente de **C** e **Java**, que usam as chaves `{` e `}` para delimitar os blocos, em Python todos os blocos são iniciados com o símbolo `:` (dois pontos) na linha superior e representados pelo acréscimo de 4 (quatro) espaços à esquerda. Sem se preocupar por enquanto com o significado das expressões `for`, `if`, `else` ou `range`, observe abaixo:

~~~python
a = 0
for i in range(30):
if a%2 == 0:
a += 1
continue
else:
if a % 5 == 0:
break
else:
a += 3
print(a)
~~~

**Linha 1**
Está mais à esquerda, assim como as linhas 2 e 11.

**Linha 2**
Todas as linhas de 3 a 10 estão dentro do bloco do for da linha 2.

**Linha 3**
Observe que a linha 3 tem um if abrindo um bloco, dentro do qual estão as linhas 4 e 5.

**Linha 6**
Por sua vez, a linha 6 tem um else abrindo outro bloco, composto pelas linhas de 7 a 10. Os blocos do if (linha 3) e do else (linha 6) estão no mesmo nível.

**Linha 7**
Mostra outro if abrindo outro bloco – composto apenas pela linha 8 – que está no mesmo nível do bloco do else da linha 9 – composto apenas pela linha 10.

**Linha 11**
Como a linha 11 está no mesmo nível da linha 2, ela não faz parte do bloco do for.

## COMENTÁRIOS
Em Python, os **comentários** podem ser de **uma linha** ou de **várias linhas**. A tabela 1 mostra as formas de limitar um comentário, além de comparar essas formas em **Python** e **C**.

| | Python | C |
| ---------- | --------- | ------------ |
| Comentários de 1 linha | Iniciados com `#` | Iniciados com `//` |
| Comentários de várias linhas | Limitados por `“””` (três aspas duplas) no início e no fim | Iniciados com `/*` e encerrados com `*/` |

#️⃣ ATENÇÃO: É importante lembrar que os comentários não são instruções a serem executadas. Então, você pode escrever de forma simples e objetiva, sem obedecer às regras de sintaxe da linguagem.

## BOAS PRÁTICAS DE PROGRAMAÇÃO
Ao começar sua jornada como programador, é importante perceber que existem algumas práticas que não são obrigatórias, mas podem ajudar muito no seu aprendizado. Além disso, podem permitir que você corrija mais rapidamente erros que podem surgir no futuro e tornam seu código mais fácil de ser compreendido por outro programador, favorecendo o trabalho em equipe. Vamos conhecer algumas delas:

- Uma prática muito importante é utilizar comentários no seu programa, explicando o que aquele trecho resolve.

- Uma característica marcante da comunidade de desenvolvedores Python é manter uma lista de propostas de melhorias, chamadas **PEP** (Python Enhancement Proposals). Dentre as PEPs, destaca-se a **PEP8**, que estabelece um guia de estilo de programação.

## Variáveis em Python
As **variáveis** são abstrações para endereços de memória que permitem que os programas fiquem mais fáceis de codificar, entender e depurar. Podemos entender que ao nomear uma variável com o identificador `x`, determinado espaço em memória passará a ter esse apelido. Em outras palavras, será possível acessar esse espaço de memória sabendo o seu apelido e, consequentemente, recuperar o valor guardado nele, que no nosso exemplo é `10`.

Uma analogia possível com o mundo real é com aquelas caixas de correio que ficam em frente às casas.

Em Python, o operador de atribuição é o `=` (símbolo de igual). A instrução `x = 10` atribui o valor `10` à variável `x`.

No prompt interativo `>>>`, digite `x = 10` e pressione a tecla [ENTER] ou [RETURN] do seu teclado. Em seguida, digite `x` e pressione a tecla [ENTER] ou [RETURN].

![figura05](https://user-images.githubusercontent.com/61624336/133180044-a4cebb34-ac4e-4924-99f8-0b3ae55f3486.png)

Observe na figura 5 que o retorno do Python Console foi 10.

Se, posteriormente, você utilizar novamente o prompt interativo `>>>` para digitar `x = 20` e pressionar a tecla [ENTER], você alterará o valor da variável `x`. Ou seja, você estará mudando o valor armazenado naquele espaço de memória, mas sem alterar seu apelido. Observe a figura 6:

![figura06](https://user-images.githubusercontent.com/61624336/133180604-727c58f9-da21-4a86-b7f9-ab8996f82ab5.png)


Atenção: Diferentemente de outras linguagens, como C ou Java, não é necessário declarar uma variável antes de utilizá-la em Python. Basta atribuir um valor inicial à variável e utilizá-la dali em diante. Embora não seja necessário declarar uma variável para utilizá-la, não é possível utilizar uma variável que não tenha recebido alguma atribuição de valor.

No prompt interativo, digite `b` e pressione a tecla [ENTER] ou [RETURN].

![figura07](https://user-images.githubusercontent.com/61624336/133181027-cf576469-58c4-4e80-bf89-2800e8362f5b.png)

Veja na figura 7 que a mensagem de erro informa que o nome `b` não foi definido. Ou seja, não é possível determinar o valor atribuído a esse nome.

## IDENTIFICADORES DE VARIÁVEIS
Os **identificadores das variáveis** podem ser compostos por **letras**, o **underline** (_) e, com **exceção** do primeiro caractere, números de `0` a `9`. Veja os exemplos:

**minhaVariavel**, **_variavel**, **salario1** e **salario1_2** são válidos.

**1variavel** e **salario-1** não são válidos.

**minhaVariavel** e **minhavariavel** são identificadores de duas variáveis distintas.

Mesmo que seja um identificador permitido, nem sempre um identificador é bom para uma variável. Tente utilizar nomes que ajudem a entender o significado da variável para ganhar tempo quando for entender o código posteriormente.


Exemplo: salario é um nome de variável melhor que s.

Algumas palavras são consideradas reservadas e não podem ser usadas como identificadores de variáveis em Python. São elas: `and`, `as`, `assert`, `break`, `class`, `continue`, `def`, `del`, `elif`, `else`, `except`, `exec`, `finally`, `for`, `from`, `global`, `if`, `import`, `in`, `is`, `lambda`, `not`, `or`, `pass`, `print`, `raise`, `return`, `try`, `while`, `with` e `yield`.

## AMARRAÇÕES (binding)
Chamamos de **amarração** (binding) a **associação** entre entidades de programação. Veja alguns exemplos:

`Variável amarrada a um valor` `Operador amarrado a um símbolo` `Identificador amarrado a um tipo`

O tempo em que a amarração ocorre é chamado de **tempo de amarração**. Cada linguagem pode ter os seguintes tempos de amarração:

### TEMPO DE PROJETO DA LINGUAGEM
Os símbolos são amarrados ao operador, como `*` (multiplicação), ou à definição das palavras reservadas.

### TEMPO DE IMPLEMENTAÇÃO
Ocorre em geral nos **compiladores**, como a definição de faixa de valores para determinado tipo.

### TEMPO DE COMPILAÇÃO
Associação da variável ao seu tipo. Lembre-se de que Python associa a variável ao tipo, como foi explicado na seção anterior.

### TEMPO DE LIGAÇÃO
A ligação de vários módulos compilados previamente, como a chamada a uma função de um módulo importado. Em **C**, utilizamos a diretiva `#include` para termos permissão de utilizar as funções de determinada biblioteca. Em Python, utilizamos o `import` para isto.

### TEMPO DE CARGA
Quando o programa é carregado. Por exemplo: endereços de memória relativos são substituídos por **endereços absolutos**.

### TEMPO DE EXECUÇÃO
Associação de valores a variáveis que dependam de entradas do usuário, por exemplo. A variável é vinculada ao valor apenas durante a execução do programa.

O momento em que ocorre a ligação pode ser classificado como **cedo** (early binding) ou **tardio** (late binding). Quanto mais cedo ocorre a ligação, maior a eficiência de execução do programa, porém menor a flexibilidade das estruturas disponibilizadas.

### Amarração de tipo
As **amarrações de tipo** vinculam a variável ao tipo do dado. Elas podem ser:

#### Estáticas
Ocorrem antes da execução e permanecem inalteradas. Em **C**, declaramos `int a`.

#### Dinâmicas
Ocorrem durante a execução e podem ser alteradas. É o caso do Python.

Veja a figura 8:

![figura08](https://user-images.githubusercontent.com/61624336/133185547-6fcf6f2f-7e4f-4213-b97e-7da196919c03.png)

Perceba que a chamada **type** (parâmetro) retorna o tipo do parâmetro informado entre parênteses. Observe que a variável valor recebeu `10` e, com isso, ficou vinculada ao tipo `int`. Porém, ao receber o valor ‘a’, passou a estar vinculada ao tipo **str** (string).

### ESCOPO DE VISIBILIDADE
O escopo define em quais partes do programa uma variável é visível. Cada nome de variável em Python tem seu escopo e fora desse escopo o nome não existe, gerando um erro quando se tenta referenciar esse nome. Quanto ao escopo, chamamos as **variáveis de globais** ou **locais**.

### Variáveis globais
Todos os nomes atribuídos no prompt interativo ou em um módulo fora de qualquer função são considerados como de **escopo global**. Por exemplo, ao executar a instrução da figura 9, a variável `x` é uma variável global.

![figura08](https://user-images.githubusercontent.com/61624336/133186462-e611ec00-cf05-4c65-aa08-39b01e6286a5.png)

### Variáveis locais
Para exemplificar o uso de variáveis com **escopo local**, vamos utilizar uma função definida pelo desenvolvedor. Não se preocupe com esse tipo de função por enquanto, você aprenderá mais detalhes posteriormente. Observe a figura 10:

![figura10](https://user-images.githubusercontent.com/61624336/133186774-2723bffc-04d8-4ffa-ab3f-21ed1a386bb7.png)

As linhas **2**, **3** e **4** compõem o bloco interno à função chamada `multiplicador()`. Embora as variáveis das linhas 2 e 7 tenham o mesmo nome, elas são abstrações a endereços de memória diferentes. Dentro da função `multiplicador()`, a chamada ao nome a recupera o valor 2. Fora da função `multiplicador()`, a chamada ao nome a recupera o valor 3. Veja a execução na figura 11:

![figura11](https://user-images.githubusercontent.com/61624336/133186957-58a33e8a-35ae-4f47-9030-ccc9474b7c23.png)

Agora, observe a função `multiplicador()` com uma pequena alteração, em que retiramos a inicialização da variável `a` dentro da função.

![figura12](https://user-images.githubusercontent.com/61624336/133187100-b86a175a-d049-44d1-ba63-8e8745331dbf.png)

Na linha 6, ao se chamar a função `multiplicador()`, a variável a será procurada. Como não existe uma variável `a` no bloco interno da função, ela é procurada como **variável global**. Uma vez encontrada, o valor recuperado é `3`. Ao executar esse exemplo, você verá:

![figura11](https://user-images.githubusercontent.com/61624336/133187512-5ddcfd2c-6fca-4283-8ec5-fc85161a29ec.png)

Usamos este exemplo para mostrar que o interpretador Python pode procurar o mesmo nome de variável em diferentes escopos. A ordem utilizada para a procura é:

1. A chamada da função delimitadora

2. Variáveis globais

3. O módulo builtins

Perceba que, se a variável `a` é inicializada na função `multiplicador()`, qualquer chamada a esse nome dentro da função resultará na referência a essa variável local. Mas seria possível alterar a variável `a` global com uma instrução dentro da função `multiplicador()`? Sim, utilizando-se a palavra **reservada global**. Veja como isso poderia ser feito na figura 14 e o seu resultado na figura 15:

![figura14](https://user-images.githubusercontent.com/61624336/133187862-28eb033b-2d83-4ab5-adf3-fe1c4e4e9c00.png)

![figura15](https://user-images.githubusercontent.com/61624336/133187955-a319888d-32fc-49e3-9f72-36860e4ba1cb.png)

### Tipos de escopo
Os tipos de escopo são:

### ESTÁTICO
O escopo é baseado na descrição textual do programa e as amarrações são feitas em tempo de compilação. É o caso de **C**, **C++** e **Java**, por exemplo.

### DINÂMICO
O escopo é baseado na sequência de chamada dos **módulos** (ou funções). Por isso, as amarrações são feitas em tempo de execução. É o caso do Python.

O fato de Python ser de escopo dinâmico traz alguns problemas, como a **perda de eficiência** – uma vez que os tipos precisam ser verificados em tempo de execução – e a redução na legibilidade – porque é difícil determinar a sequência exata de todas as **chamadas de função**.

### TEMPO DE VIDA
Embora **escopo** e **tempo de vida** tenham uma relação próxima, eles são conceitos diferentes. Observe:

`Escopo é um conceito textual` x `Tempo de vida é um conceito temporal`

As **variáveis globais** têm o tempo de vida que é o de execução do programa, ao passo que as variáveis locais somente existem no intervalo de duração da função ou do bloco a que se limitam.

## CONSTANTES
Em Python, não existe o conceito de constante (Arrays). Se você precisar de uma constante ao longo de sua jornada como programador, atribua o valor a uma variável e tome cuidado para não mudar esse valor.


Dica: Uma dica é iniciar o nome dessa variável com c_ ou utilizar todas as letras maiúsculas, o que vai diferenciar essa variável das outras. Por exemplo, é possível utilizar a expressão c_PI = 3.141592 para armazenar o valor de PI e agilizar o cálculo de área e perímetro de um círculo, ou utilizar a expressão PRECISION = 0.001 para armazenar a precisão a ser utilizada em qualquer cálculo matemático no seu programa.

É importante ficar atento ao uso correto das variáveis, especialmente observando as questões de **escopo** e **visibilidade**, para evitar que algum cálculo seja realizado corretamente, mas com resultado diferente do esperado por você ao programar.

~~~python
def func():
x = 1
print(x)
x = 10
func()
print(x)
~~~

## Os tipos de dados e as expressões em Python
Você será apresentado aos **tipos padrão incorporados** ao **interpretador** Python. Os principais tipos internos são **numéricos**, **sequenciais** e **dicionários**. **Classes**, **instâncias** e **exceções** também são _tipos padrão_, mas não entraremos em detalhes neste tema. Para ter nosso primeiro contato com expressões em Python, use o prompt interativo `>>>`.

Digite a expressão algébrica 5 + 8 e pressione a tecla [ENTER]. Observe o resultado na figura 16:

![figura16](https://user-images.githubusercontent.com/61624336/133260245-e0d13a52-d5aa-4ec9-9b15-bd9fb3adeebe.png)

O **Python Console** permite que você calcule expressões algébricas como uma **calculadora**, além de executar instruções básicas em Python.

## TIPOS NUMÉRICOS
Existem três tipos numéricos distintos em Python: **inteiros**, **números de ponto flutuante** e **números complexos**. Além disso, os **booleanos** são um subtipo dos inteiros.

### O tipo int
É o tipo usado para manipular números inteiros. Fazendo uma analogia com a Matemática, o tipo `int` é usado para elementos do conjunto dos inteiros (Z).

Diferentemente de outras linguagens, como **C** ou **Java**, a **linguagem Python** não limita o tamanho de uma variável de qualquer tipo, logo, não existe um valor inteiro máximo definido. O limite depende da quantidade de memória disponível no computador. De volta ao Python Console, veja algumas coisas interessantes.

Digite 1_000_000 e pressione a tecla [ENTER].

![figura18](https://user-images.githubusercontent.com/61624336/133261203-da26e2ac-befa-4663-b3ef-fe783af0c10f.png)

### O tipo float
É o tipo usado para manipular números com parte inteira e parte decimal, chamados de **números de ponto flutuante**. Fazendo uma analogia com a Matemática, o tipo **float** é usado para elementos do conjunto dos reais (R).

Para diferenciar um número real de um inteiro, é possível utilizar a parte decimal zerada. No prompt interativo `>>>`, digite `type(50.0)` e pressione a tecla [ENTER].

![figura20](https://user-images.githubusercontent.com/61624336/133261779-8db1954f-6b83-46ee-8225-c817cf7fa619.png)


Atenção: Vale ressaltar que devemos usar o ponto para separar a parte inteira da parte decimal, e não a vírgula.

No prompt, digite **50.0** (com ponto) e pressione [ENTER]. Em seguida, digite **50,0** (com vírgula) e pressione a tecla [ENTER].

![figura20](https://user-images.githubusercontent.com/61624336/133343981-0aef241f-35d8-46d9-a720-23fc508fef4a.png)

Ao usar a vírgula como separador em Python, o que ocorre, na verdade, é a criação de uma tupla de dois elementos, e não o tipo _float_. Você verá mais detalhes sobre tuplas em um momento posterior.

Embora os tipos _int_ e _float_ sejam semelhantes, por tratarem de números, eles têm propriedades um pouco diferentes. Em expressões algébricas, sempre que **somamos**, **subtraímos** ou **multiplicamos** apenas elementos do tipo `int`, o resultado é `int`. Porém, basta um operando do tipo `float` para que o resultado seja `float`. Observe a figura 21:

![figura22](https://user-images.githubusercontent.com/61624336/133344221-6bdae2ec-517e-4a15-9f44-87f0711949e7.png)

Vamos analisar a **exponenciação**. Para realizar essa operação matemática, utilizamos o operador `**`. Veja a figura 22:

![figura22](https://user-images.githubusercontent.com/61624336/133344278-8c9d60f4-415e-4efc-adf4-bc1bb8569f49.png)

Veja que basta que a base seja `float` para que o resultado também o seja.


Atenção: Diferentemente de outras linguagens, como C, a divisão de dois números inteiros não necessariamente tem resultado inteiro.

Digite 5/2 e pressione [ENTER].

![figura23](https://user-images.githubusercontent.com/61624336/133345150-22243f10-4ddc-4eb6-91a6-da2a3124c67e.png)

Para obter o **quociente inteiro** e o **resto**, quando dois inteiros são _divididos_, é necessário utilizar os operadores `//` e `%`, respectivamente. Ao dividir `21` por `2`, temos quociente `10` e resto `1`. Observe a figura 24:

![figura24](https://user-images.githubusercontent.com/61624336/133345395-26202e09-187f-41e7-a52f-01a43ac09d5a.png)

### O tipo complex
É o tipo utilizado para manipular **números complexos**, na forma `x + yj`, sendo `x` a **parte real** e `y` a **parte imaginária** do **complexo**.

Veja dois exemplos de variáveis do tipo complex nas figuras 25 e 26, em que a parte real é `2` e a parte imaginária é `5`:

![figura25](https://user-images.githubusercontent.com/61624336/133345607-c9e27b60-a39a-4aed-b81f-dba21be21db6.jpg)
![figura26](https://user-images.githubusercontent.com/61624336/133345615-4ce970ef-c848-4f6c-a872-c2b27c4366f7.jpg)

A chamada `r.conjugate()` retorna o conjugado do número complexo `r`, em que a _parte real_ é **mantida** e a _parte imaginária_ tem o seu **sinal trocado**.

### O tipo bool
Uma expressão algébrica, como vimos nos exemplos dos tipos `int` e `float`, é avaliada como um número, seja desses tipos ou de outro tipo numérico admitido em Python. Porém, utilizar **expressões não algébricas** também é bastante comum. E uma boa notícia é que Python pode avaliar expressões desse tipo também. Essa é uma diferença entre **Python** e outras linguagens, como **C**, por exemplo, em que não existe o tipo **bool**.

No prompt interativo `>>>`, digite a expressão `2 < 3` e pressione [ENTER]. Observe o resultado na figura 27:

![figura27](https://user-images.githubusercontent.com/61624336/133345820-5c5ab274-4751-49d2-aed4-49b07c255fe3.png)

Repare que o resultado dessa expressão não é um número, mas sim a palavra `True`. Caso você colocasse a expressão `2 > 3`, o resultado seria `False`, como pode ver na figura 28.

![figura28](https://user-images.githubusercontent.com/61624336/133346388-3b31df7a-ba34-4a03-9625-8541b9938a93.png)


Saiba mais: As expressões que você viu nos dois exemplos são chamadas de expressões booleanas. Trata-se de expressões que podem ser avaliadas com um dos dois valores booleanos: True ou False. Assim, em Python, existe o tipo bool, utilizado para permitir o tratamento de expressões como essas.

Agora, vamos ver o operador `not`, que é um **operador unário**, ou seja, só precisa de **um operando**. Esse operador inverte o valor booleano, ou seja, se o valor original for `True`, `not(valor)` terá o valor `False`. **E vice-versa**.

No prompt interativo `>>>`, digite a expressão `not(2 < 3)` e pressione [ENTER].

![figura29](https://user-images.githubusercontent.com/61624336/133347017-7d5745e8-9436-4ef8-893c-456b624d6ed1.png)

É possível também escrever **expressões booleanas** compostas, utilizando conectivos como `E` `OU`. Vamos ver mais detalhes sobre essas expressões ainda neste módulo.

## Operadores numéricos

### Operadores matemáticos/ aritméticos
Os **operadores matemáticos** são muito semelhantes àqueles que vimos ao longo de nossa jornada como estudantes, aprendendo **Álgebra** e **Aritmética** na escola. Existem algumas pequenas diferenças, como a **divisão** (que pode ser a usual ou a divisão inteira). Mas é possível identificar operações que fizemos ao longo de toda nossa vida. A tabela 2 lista os operadores de expressão aritmética disponíveis em Python.



Operação matemática
Símbolo usado
Exemplo


Equação
Resultado


Soma
+
2.5 + 1.3
3.8


Subtração
-
2.5 - 1.3
1.2


Multiplicação
*
2.5 * 1.3
3.25


Divisão
/
2.5 / 1.3
1.923076923076923


Divisão inteira
//
9/2
4


Resto na divisão inteira
%
9 % 2
1


Valor absoluto
abs(parâmetro)
abs(-2.5)
2.5


Exponenciação
**
2**4
16

### Operadores de Comparação

Além das operações algébricas, é possível realizar **operações de comparação**. Os **operadores de comparação** têm como resultado um valor **booleano** (`True` ou `False`). Observe a tabela 3:


Símbolo usado
Descrição


<
Menor que


<=
Menor ou igual a


>
Maior que


>=
Maior ou igual a


==
Igual


!=
Não igual


Atenção! Cabe observar que o operador utilizado para comparar se dois valores são iguais é o ==, ou seja, duplo sinal de igual. Tome cuidado para não confundir com o operador de atribuição, que é representado pelo sinal de igual apenas uma vez =.

Existe outra lista de operadores que executam operações matemáticas, mas, além disso, atualizam o valor da variável utilizada. Eles são chamados de **operadores compostos**. Para mais funções matemáticas, você pode utilizar os módulos matemáticos math e fractions.

### Operadores booleanos
As **expressões booleanas** são aquelas que podem ter como resultado um dos valores booleanos: `True` ou `False`. É comum utilizarmos os **operadores de comparação** em expressões booleanas, mas não só eles.

Assim como é possível escrever expressões algébricas complexas _concatenando_ diversas expressões menores, podemos escrever expressões booleanas grandes, com os operadores `and`, `or` e `not`. Observe o comportamento dos operadores booleanos nas tabelas 4, 5 e 6.

#### Operador `not`


p
not(p)


True
False


False
True

#### Operador `and`


p
q
p or q


True
True
True


True
False
True


False
True
True


False
False
False

#### Operador `or`


p
q
p and q


True
True
True


True
False
False


False
True
False


False
False
False

## TIPOS SEQUENCIAIS
Existem três tipos sequenciais básicos em Python: **listas**, **tuplas** e **objetos range**. Além dos tipos básicos citados, existe um tipo especial criado para tratamento de dados textuais: o tipo `str` (string).

Assim como em **C** ou **Java**, a indexação dos itens é iniciada com `0` e cada item tem o seu índice incrementado uma unidade em relação ao item anterior. Porém, Python também permite a indexação com valores negativos. O valor `-1` é o índice do último item, e cada item anterior é decrementado de uma unidade em relação ao sucessor. Observe a tabela 7:


índice
0
1
2
3
4


s
t
e
s
t
e


índice negativo
-5
-4
-3
-2
-1

## Strings
Em uma variável do tipo `str`, é possível **armazenar letras, números, espaços, pontuação e diversos símbolos**. Diferentemente da **linguagem C**, _não existe_ o tipo `char`. **Cada caractere em Python é uma string**. Para delimitar uma string, podemos utilizar:

### Aspas simples
```python
‘uma string'
```

### Aspas duplas
```python
“uma string”
```

### Aspas simples triplas
```python
‘’’uma string’’’
```

### Aspas duplas triplas
```python
“””uma string”””
```

A Figura 30 ilustra um exemplo de delimitadores de strings:

![figura30](https://user-images.githubusercontent.com/61624336/133940623-b524ef49-5d89-4095-9bba-91f8d61efb0f.png)

Existem alguns métodos interessantes para **tratar strings em Python**. Entre eles, ressaltamos:

### `upper`
Transforma todas as letras em **maiúsculas**.

### `lower`
Transforma todas as letras em **minúsculas**.

### `split`
Quebra a string em **substrings**.

Veja os exemplos a seguir:

![figura31](https://user-images.githubusercontent.com/61624336/133940776-28b08988-4471-4e60-8db4-d9d865b16868.png)


Saiba mais: A lista gerada com o método ,split() tem três elementos, porque a string original tinha três palavras.

## Listas
**Listas** são _sequências mutáveis_, normalmente usadas para **armazenar coleções de itens homogêneos**. Uma **lista** pode ser criada de algumas maneiras, tais como:

### []
Usando um par de colchetes `[]` para denotar uma **lista vazia**.

### [a], [a, b, c]
Usando colchetes, separando os itens por vírgulas.

### [x for x in iterable]
Usando a **compreensão de lista**.

### list() ou list(iterable)
Usando o **construtor** do tipo _list_.


Saiba mais: iterable pode ser uma sequência, um container que suporte iteração ou um objeto iterador. Por exemplo, list('abc') retorna ['a', 'b', 'c'] e list( (1, 2, 3) ) retorna [1, 2, 3]. Se nenhum argumento for passado, o construtor cria uma lista vazia: [ ].

## Tuplas
**Tuplas** são _sequências imutáveis_, tipicamente usadas para **armazenar coleções de itens heterogêneos**. Elas são aplicadas também quando é necessário utilizar uma sequência imutável de dados homogêneos. Uma tupla pode ser criada de algumas maneiras, tais como:

- `()` Usando um par de parênteses para denotar uma tupla vazia.

- `a, b, c` ou `(a, b, c)` Separando os itens por vírgulas.

- `tuple()` ou `tuple(iterable)` Usando o construtor do tipo `tuple`.

Novamente, **iterable** pode ser uma sequência, um container que suporte **iteração** ou um **objeto iterador**. Por exemplo, `tuple('abc')` retorna `('a', 'b', 'c')` e `tuple( [1, 2, 3] )` retorna `(1, 2, 3)`. Se nenhum argumento for passado, o construtor cria uma tupla vazia: `()`.


Atenção: Note que o uso das vírgulas é o que gera a tupla, e não o uso de parênteses. Os parênteses são opcionais, exceto no caso em que queremos gerar uma tupla vazia.

## Range
O tipo **range** representa uma _sequência imutável de números e frequentemente é usado em loops de um número específico de vezes_, como o `for`.

Ele pode ser chamado de maneira simples, apenas com **um argumento**. Nesse caso, a sequência começará em `0` e será incrementada de uma unidade até o limite do parâmetro passado (exclusive). Por exemplo, `range(3)` cria a sequência `(0, 1, 2)`.

Para que a sequência não comece em `0`, podemos informar o início e o fim como parâmetros, lembrando que o parâmetro fim não entra na lista (exclusive o fim). O padrão é incrementar cada termo em uma unidade. Ou seja, a chamada `range(2, 7)` cria a sequência `(2, 3, 4, 5, 6)`.


Saiba mais: Também é possível criar sequências mais complexas, indicando os parâmetros de início, fim e passo, nessa ordem. O passo é o valor que será incrementado de um termo para o próximo. Por exemplo, range(2, 9, 3) cria a sequência (2, 5, 8).

Para que a sequência não comece em 0, podemos informar o início e o fim como parâmetros, lembrando que o parâmetro fim não entra na lista (exclusive o fim). O padrão é incrementar cada termo em uma unidade. Ou seja, a chamada range(2, 7) cria a sequência (2, 3, 4, 5, 6).

## Operadores sequenciais comuns
Os **operadores sequenciais** permitem a _manipulação dos tipos sequenciais_, inclusive as **strings**. Vale ressaltar a sobrecarga dos operadores `+` e `*`, que realizam operações diferentes quando os operandos são **numéricos** ou **sequenciais**.


Exemplo: O operador == verifica se as strings dos dois lados são iguais. Porém, os operadores < e > comparam as strings usando a ordem do dicionário.


A tabela a seguir traz um pequeno conjunto dos operadores disponíveis em Python para **manipulação de sequências**. Lembre-se de que você pode utilizar o utilitário `help` no Python Console para verificar a lista completa. Para isso, basta digitar `help(str)` e pressionar [ENTER] no teclado.


Uso
Resultado


x in s
True se x for um subconjunto de s


x not in s
False se x for um subconjunto de s


s + t
Concatenação de s e t


n*s
Concatenação de n cópias de s


s[i]
Caractere de índice i em s


len(s)
Comprimento de s


min(s)
Menor item de s


max(s)
Maior item de s

## DICIONÁRIOS
Os **dicionários** permitem que itens de uma sequência recebam índices definidos pelo usuário. Um dicionário contém pares de (chave, valor). O formato geral de um objeto dicionário é:

```python
{:, :, ..., :}
```

**Exemplo**: Poderíamos criar um dicionário em que cada pessoa fosse representada pelo seu **CPF**, com **nome** e **sobrenome**. Para isso, teríamos:

![figura32](https://user-images.githubusercontent.com/61624336/135189834-c300d0a1-a345-444c-96f3-bbe980b68180.png)

Na figura 32, o dicionário tem **3 entradas**. Observe como foi possível recuperar **nome** e **sobrenome** de uma entrada, baseado na chave informada `111222333-44`.

## PRECEDÊNCIA DE OPERADORES
Ao escrever uma expressão algébrica, o programador pode utilizar a **precedência de operadores** existente em Python (implícita) ou **explicitar** a ordem em que ele deseja que a expressão seja avaliada.

**Exemplo**: Por exemplo, a expressão `3 + 2 * 5` tem como resultado `25` ou `13`? Aprendemos no ensino fundamental que as operações de **produto** e **divisão** têm precedência sobre as operações de **soma** e **subtração**. Ou seja, **um produto** será realizado **antes de uma soma**, na mesma expressão. Assim, a expressão acima tem como resultado `13`. Isso ocorre sempre que não forem explicitadas outras relações de precedência com o uso de parênteses. Caso o programador quisesse forçar que a soma ocorresse primeiro, ele deveria escrever assim: `(3 + 2) * 5`.

Sempre que o programador quiser forçar a ocorrência de uma operação antes de outras, ele pode utilizar os parênteses para aumentar a prioridade sobre ela. A tabela a seguir traz as relações de precedência entre os operadores, com as linhas mais altas tendo prioridade sobre as linhas mais baixas. Ou seja, elas ocorrem primeiro. Dentro da mesma linha, a precedência é da esquerda para a direita.


Operador
Descrição


[expressões ...]
Definição de lista


x[ ], x[índice : índice]
Operador de indexação


**
Exponenciação


+x, -x
Sinal de positivo e negativo


*, /, //, %
Produto, divisão, divisão inteira, resto


+, -
Soma, subtração


in, not in, <, <=, >, >=, <>, !=, ==
Comparações, inclusive a ocorrência em listas


not x
Booleano NOT (não)


and
Booleano AND (e)


or
Booleano OR (ou)


> **Atenção!** É importante ficar atento ao uso correto dos operadores, respeitando a precedência entre eles, para evitar que algum cálculo seja realizado corretamente, mas com resultado diferente do esperado por você ao programar.

**Exemplo**: Um número do tipo int pode ser visto como um float com a parte decimal nula. Porém, o inverso não é verdade. Ou seja, o conjunto dos inteiros (int) é um subconjunto do conjunto dos reais (float). Assim, a expressão 5 + 0.68 – que envolve um int e um float – tem como resultado 5.68. O inteiro 5 é convertido pelo Python para o número de ponto flutuante 5.0 antes que a soma (de dois valores float) seja realmente efetuada.

### Conversão Implícita
Uma **conversão implícita não intuitiva** é a dos valores booleanos `True` e `False` em inteiros, respectivamente, `1` e `0`. Veja os exemplos a seguir:

![figura33](https://user-images.githubusercontent.com/61624336/135546575-8632668b-5639-41f5-8876-b60d0bdc6d6f.png)

Com isso, podemos perceber a seguinte relação entre os tipos `bool`, `int` e `float`:

![figura34](https://user-images.githubusercontent.com/61624336/135546748-bfdd145b-3ee7-4155-a232-a47a4e2d82b4.png)

### Conversão Explícita
Além das **conversões implícitas**, o programador também pode usar as **conversões explícitas**, quando ele força que o valor seja tratado como de determinado tipo. Para isso, é necessário usar o construtor do tipo desejado, com o valor passado como parâmetro (entre parênteses). Veja o exemplo a seguir:

![figura35](https://user-images.githubusercontent.com/61624336/135546878-6c6ce281-2d63-4a24-9b4e-20fded847fa8.png)

O `int 2` pode ser tratado naturalmente como o `float 2.0`, basta acrescentar a parte decimal nula. Porém, ao tentar tratar um `float` como `int`, ocorre a remoção da parte decimal.

> **Atenção!** Fique atento, porque não é uma aproximação para o inteiro mais próximo, e sim o **truncamento**.

Agora que você já viu os principais tipos de dados suportados em Python, vamos exercitar e verificar o aprendizado.

1. Considere a expressão a seguir: `2 + 3 – 4 ** 2 + 5 / 2 – 5 // 2`

Assinale a opção com o valor correto dessa expressão em Python.

Resposta: -10.5

Lembre-se que o operador ** tem precedência maior do que os operadores / e //, os quais, por sua vez, têm precedência sobre + e -. Ou seja, primeiro será efetuada a exponenciação (4**2), depois as divisões, comum (5/2) e inteira (5//2), para posteriormente serem efetuadas as somas e subtrações.

2. (Adaptada de COMPERVE/2019/UFRN/Engenharia da Computação) Python é uma linguagem interpretada muito utilizada. Não requer tipagem de variáveis e sua sintaxe indentada favorece a organização do código. Uma das suas funcionalidades mais poderosas são as listas. Considere o código em Python do quadro abaixo:

![mod3p2](https://user-images.githubusercontent.com/61624336/135548998-533397a9-4fd6-4d9c-8e0f-f51b1c156753.png)

Resposta: 2 e 4

O operador + realiza operações de soma para tipos numéricos e concatenação para tipos sequenciais. Assim, a variável a na linha 1 passa a ser composta dos itens ‘UF’ e ‘RN’. Assim, a chamada len(a) retorna o tamanho 2, número de elementos de a. De forma semelhante, o operador * realiza operações de multiplicação para tipos numéricos e concatenação de cópias para tipos sequenciais. Assim, a variável b na linha 3 passa a ser a lista ['4', '4', '4', '4']. E a chamada len(b) retorna o tamanho 4, número de elementos de b.


## Entrada e saída de dados em Python
Já vimos, de maneira básica, como podemos atribuir valor a uma variável, no módulo 2. Vamos agora conhecer outras formas de atribuição.

## Sentenças de atribuição

### Atribuição simples
Chamamos de **atribuição simples** a forma que já utilizamos neste tema, com uma expressão parecida com `x = 10`. Nessa atribuição, a variável `x` recebe o valor `10`.

### Atribuição múltipla
Python também permite a **atribuição múltipla**, ou seja, mais de uma variável receber atribuição na mesma linha. Veja o exemplo na figura 36:

![figura36](https://user-images.githubusercontent.com/61624336/135550002-e383e5de-b764-4d09-b497-93ee47013575.png)

> **Atenção!** Observe que as variáveis `x` e `y` receberam atribuição na mesma instrução, com a variável `x` armazenando o valor `2`, e a variável `y` armazenando o valor `5`.

### Operadores de atribuição compostos
Os **operadores de atribuição compostos** executam operações matemáticas e atualizam o valor da variável utilizada. Por exemplo, veja a figura 37:

![figura37](https://user-images.githubusercontent.com/61624336/135550875-4cbba501-1415-4f7e-a99e-b34b35c9347a.png)

A variável `x` inicialmente recebeu o valor `10`. Em seguida, a instrução `x = x + 1`, que causa estranheza quando lembramos da matemática aprendida ao longo da vida, é muito comum quando estamos programando. Essa instrução significa “acrescente uma unidade ao valor de `x` e guarde este resultado na própria variável `x`”. Como `x` valia `10`, o resultado do lado direito do operador (`=`) é `11`. Esse resultado é, então, armazenado na própria variável `x`.

Essa **operação de acrescentar** determinado valor a uma variável e armazenar o resultado na própria variável poderia ser feita com o operador `+=` (mais igual). Veja a figura 38:

![figura38](https://user-images.githubusercontent.com/61624336/135552211-7684c2a5-1dd7-48e8-b387-cfa5731ad05e.png)

Na tabela 10, estão os operadores compostos disponíveis em Python. Considere a variável `x`, com o valor inicial `10`, para verificar os resultados.


Nome
Símbolo usado
Exemplo


Instrução
Resultado


Mais igual
+=
x += 2
x passa a valer 12


Menos igual
-=
x -= 2
x passa a valer 8


Vezes igual
*=
x *= 2
x passa a valer 20


Dividido igual
/=
x /= 2
x passa a valer 5


Módulo igual
%=
x %= 3
x passa a valer 1

> **Atenção!** Diferente de C, em Python não é possível incrementar ou decrementar uma variável com um operador unário, como o ++ ou --.

## Troca de variáveis
Um dos problemas iniciais que envolvem atribuição de valores a variáveis é a troca entre duas delas. Suponha que as variáveis `a` e `b` armazenem, respectivamente, os valores `1` e `2`. Caso quiséssemos inverter os valores em linguagens como **C** ou **Java**, seria necessário usar uma variável auxiliar, com uma sequência de instruções exibida na figura a seguir:

![figura39](https://user-images.githubusercontent.com/61624336/135555036-17ad052e-35b6-4138-b852-52896b5b38eb.png)

Em Python, é possível fazer essa troca de uma maneira muito mais fácil. Veja o uso da atribuição múltipla, nesse caso, na figura a seguir:

![figura40](https://user-images.githubusercontent.com/61624336/135555122-4a80fd61-9d9b-4b9b-a867-748b293ccd98.png)

## 🐍 O PRIMEIRO PROGRAMA EM PYTHON 🖥️

Para escrever um programa em Python, será essencial utilizar as formas de **saída de dados** para exibir ao usuário mensagens e resultados de operações. Caso você deseje que o usuário informe algum dado para que seu programa processe, será necessário utilizar as formas de entrada de dados.

### Saída de dados com a função `print()`
A função `print()` em Python atua de forma semelhante à `printf()` em **C**. Para um programador iniciante, as maiores diferenças entre elas são:

- Duas chamadas da `print()` em Python são impressas na tela em linhas diferentes, sem a necessidade do uso do caractere `\n` para pular a linha, como ocorre na `printf()` em **C**.

- Uma chamada da `print()` em Python permite a impressão de valores de variáveis sem a indicação do formato, como ocorre na `printf()` em **C**, quando precisamos escrever `%c`, `%d` ou `%f`, por exemplo.

Para escrever seu **Hello, World!** em Python, digite a seguinte linha, exatamente como está escrita:

~~~python
print(“Hello, World!”)
~~~

ou

~~~python
print('Hello, World!')
~~~

Para divulgar a mensagem no terminal ou prompt de comando, basta ir até a pasta onde está localizado o arquivo e inserir o comando:

```
python hello-world.py
```

Veja que foi impresso no **console** exatamente o que colocamos entre aspas, ao chamar a função `print()`. Essa é a primeira forma de saída de dados: usar a função `print()` com uma string sendo passada como parâmetro (entre os parênteses). É importante perceber que a função `print()`, além de imprimir a string, também salta o cursor para a próxima linha.

Como você deve ter percebido, o que a função `print()` recebeu entre parênteses foi uma string. Ou seja, poderíamos ter passado para ela uma string definida anteriormente, como no exemplo a seguir:

![figura45](https://user-images.githubusercontent.com/61624336/135556658-5327f51b-cd98-4e9d-93e4-ca1449a41cac.png)

Também poderíamos ter passado como parâmetro uma variável definida anteriormente. A função `print()` vai trabalhar com o valor dessa variável. Observe as figuras 46 e 47:





## ENTRADA DE DADOS COM A FUNÇÃO `INPUT()`
Quando o programador quiser que o usuário entre com algum valor, ele deverá exibir na tela o seu pedido. Em **C**, é necessário utilizar a função `printf()` para escrever a solicitação ao usuário e a função `scanf()` para receber a entrada e armazenar em uma variável. Em Python, é possível utilizar a função `input()`. Ela tanto exibe na tela o pedido, como permite que o valor informado pelo usuário seja armazenado em uma variável do seu programa. Analise a figura 48:

![figura48](https://user-images.githubusercontent.com/61624336/135557070-e2632f21-e632-4ed8-8e01-3a0ba88e4694.png)

A **linha 1** fará com que a frase Entre com seu nome: seja exibida no console, mas a execução do programa fica travada até que o usuário aperte [ENTER] no teclado. Tudo o que foi digitado até o [ENTER] vai ser armazenado na variável nome. A linha 2 fará a exibição do conteúdo da variável nome. Veja o resultado no console, com o usuário tendo digitado `Fulano de Tal`.

![figura49](https://user-images.githubusercontent.com/61624336/135557120-0a3212eb-c88d-4bce-bf43-368ba46ebeb7.png)

> **Atenção!** É importantíssimo perceber que a função `input()` trata **tudo o que for digitado** pelo usuário como uma `string`, armazenando na variável designada pelo programador para isso. Mesmo que o usuário entre com apenas uma letra ou um número, isso será armazenado como uma string na variável.

Vamos analisar o exemplo a seguir:

![figura50](https://user-images.githubusercontent.com/61624336/135557245-cbb1e7e4-57d5-48e0-8062-9906350d4707.png)

Veja o console quando o programa é executado:

![figura51](https://user-images.githubusercontent.com/61624336/135557287-6b51b6ed-1854-44f1-bdc7-03461b850344.png)

O usuário digitou `3` e [ENTER]. Mesmo sendo um valor, a variável numero trata como a string ‘3’. Isso impede que seja realizada a operação de soma com o inteiro `2`, por exemplo. Poderíamos também usar a instrução `print(type(numero))` na **linha 2** para confirmar. Veja:



### A função `eval()`
A função `eval()` recebe uma string, mas trata como um valor numérico. Veja o exemplo a seguir:

![figura54](https://user-images.githubusercontent.com/61624336/135561204-f9edca85-9c7c-41be-9d26-9ffc439c84ff.png)

Mesmo tendo recebido a string `'1+2'` como parâmetro, a função `eval()` efetuou a soma de `1` com `2`. Observe que confirmamos que `s` é uma string com a instrução `type(s)`.

Para tratar a entrada do usuário como um número e, com isso, realizar operações algébricas, por exemplo, é necessário utilizar a função `eval()` em conjunto com a `input()`. Veja o próximo exemplo:



## SAÍDA FORMATADA DE DADOS
Quando desejamos que a saída siga determinado padrão – por exemplo, de hora ou de data – existem algumas possibilidades para usar a função `print()`. É sempre possível utilizar a concatenação de strings, com o operador `+`, para montar a frase como quisermos. Suponha que tenhamos as seguintes variáveis:
`hora = 10 minutos = 26 segundos = 18`

Poderíamos chamar a função `print()` com o separador `:` da seguinte forma:

![figura59](https://user-images.githubusercontent.com/61624336/135562269-f0d87927-e31b-447b-bf04-3c5e5949e718.png)

Porém, existe outra possibilidade, usando o método `format()`. Ele permite que a chamada à função `print()` fique muito parecida com as chamadas à função `printf()` em **C**, com passagem de parâmetros a serem colocados em ordem na string. Com o método `format()`, podemos montar a string com as chaves `{}` indicando onde entrarão valores, passados como parâmetros separados por vírgulas.

![figura60](https://user-images.githubusercontent.com/61624336/135564853-cfe792e0-5ca1-471c-8287-cce8be38626d.png)

É importante observar que a quantidade de chaves precisa ser igual à quantidade de variáveis passadas como parâmetros no método `format()`. Seria muito bom se não precisássemos nos preocupar com essa correspondência para evitar erros bobos. E isso é possível! Para tornar a saída formatada ainda mais intuitiva, basta utilizar a letra ‘f’ no início dos parâmetros da função `print()` e colocar cada variável dentro das chaves na posição em que deve ser impressa. Veja como fica ainda mais fácil entender:

![figura61](https://user-images.githubusercontent.com/61624336/135564963-eb56ae84-9186-4506-8d01-59c8e29db2eb.png)

Também é possível especificar a largura de campo para exibir um inteiro. Se a largura não for especificada, ela será determinada pela quantidade de dígitos do valor a ser impresso. Veja a figura 62:

![figura62](https://user-images.githubusercontent.com/61624336/135565052-006829ba-5624-4ca2-892a-8808cec44e57.png)

Observe que os valores `10` e `100` foram impressos com espaços em branco à esquerda. Isso ocorreu porque definimos que a primeira variável deveria ser impressa com `4` espaços com `{:4}` (`2` foram ocupados e `2` ficaram em branco), e que a segunda variável deveria ser impressa com `5` espaços com `{:5}` (`3` foram ocupados e `2` ficaram em branco).

### Saiba mais
Também é válido perceber que o padrão é alinhar os valores à direita do espaço reservado para a impressão da variável.

O método `format()` também pode ser usado para imprimir valores de ponto flutuante com a precisão definida. Veja a figura 63:

![figura63](https://user-images.githubusercontent.com/61624336/135565295-50303a92-2571-434f-a5d2-deb9862f6b9e.png)

Ao usar `{:8.5}`, estamos determinando que a impressão será com `8` espaços, mas apenas `5` serão utilizados.

### Impressão de sequências
**Python** também permite a impressão de sequências com mais possibilidades que **C**, incluindo as strings. Para imprimir um vetor em **C**, por exemplo, precisamos chamar a `printf()` tem o item. Em Python, basta chamar a função `print()` passando como parâmetro a sequência. Veja a figura 64:

![figura64](https://user-images.githubusercontent.com/61624336/135565470-873c3eaf-c533-4c6b-8460-898358f0350f.png)

Para imprimir uma **substring**, por exemplo, basta utilizar os colchetes `[]` para indicar o intervalo de índices que deve ser impresso. Vale lembrar que o primeiro caractere da string é indexado com `0`. Veja a figura 65:

![figura65](https://user-images.githubusercontent.com/61624336/135565532-f6321141-e722-4414-9ca7-721ac7c6b3b3.png)

> **Atenção!** Usar `[0:4]` provoca a impressão dos índices `0, 1, 2 e 3`, **mas não** do índice 4. Analogamente, usar `[2:8]` provoca a impressão dos índices de `2 a 7`, mas não do `8`.

Também é possível imprimir a string como lida da direita para a esquerda. Para isso, deve-se utilizar `[: : -1]`. Esse valor `-1` indica que a leitura dos caracteres será feita no sentido oposto ao tradicional. Observe a figura 66:

![figura66](https://user-images.githubusercontent.com/61624336/135565755-38814479-bcf7-4711-a96d-eed7c616be39.png)

> **Atenção!** Fique atento quando utilizar o intervalo na impressão no sentido inverso, porque os limites do intervalo devem respeitar esse sentido.

# 🐍 [Python] Estruturas de decisão e repetição em Python
As **estruturas de controle** permitem selecionar quais partes do código serão executadas – chamadas de **estruturas de decisão** – e **repetir blocos de instruções** com base em algum critério, como **uma variável de controle** ou a **validade de alguma condição** – chamadas de **estruturas de repetição**. Neste módulo, vamos conhecer as **estruturas de decisão** e de **repetição em Python**.

## TRATAMENTO DAS CONDIÇÕES
As **estruturas de decisão** e de **repetição** possuem sintaxes bastante semelhantes em **C** e em **Python**. Mesmo com essa grande semelhança, existe uma diferença crítica no tratamento das condições. Diferentemente da linguagem **C**, **Python** oferece o tipo `bool`. Por isso, cabe ressaltar a diferença de comportamento das duas linguagens nesse tratamento.


Python
C


Existe o tipo bool
Não existe o tipo bool


True
Qualquer valor diferente de 0 (zero)


False
0 (zero) ou vazio

> **Atenção!** Observe que o fato de haver o tipo `bool` em Python permite que as condições sejam tratadas como **verdadeiras** ou **falsas**, o que não é exatamente igual em **C**.

## AS ESTRUTURAS DE DECISÃO `IF`, `IF-ELSE` E `ELIF`
Em Python, é possível utilizar as estruturas de decisão `if` e `if-else` da mesma forma que em **C**. A diferença principal é o modo de delimitar os blocos de instruções relativos a cada parte da estrutura. Observe a Tabela 2 e a Tabela 3:


Python
C


if :
if {


Instruções com 4 espaços de indentação
A indentação não é exigida


Instrução fora do if
}


Python
C


if :
if {


Instruções com 4 espaços de indentação (caso a condição seja verdadeira)
Bloco 1 (caso a condição seja verdadeira). A indentação não é exigida


else:
} else {


Instruções com 4 espaços de indentação (caso a condição seja falsa)
Bloco 2 (caso a condição seja falsa). A indentação não é exigida


Instrução fora do if
}

Python também oferece a estrutura `elif`, que permite o teste de duas condições de forma sequencial. Essa estrutura não existe em **C**, sendo necessário o encadeamento de estruturas `if-else`. Em geral, o formato da estrutura `elif` é:

```python
1 """
2 if :
3 Bloco de código que será executado caso condição seja True
4 elif :
5 Bloco de código que será executado caso condição 1 seja False e condição 2 seja True
6 else:
7 Bloco de código que será executado caso condição 1 seja False e condição 2 seja False
8 Instrução fora do if
9 """
```

Veja uma implementação possível com a estrutura `elif` na Figura 1:

```python
idade = eval(input('Informe a idade da criança: '))
if idade < 5:
print('A criança deve ser vacinada contra a gripe.')
print('Procure o posto de saúde mais próximo.')
elif idade == 5:
print('A vacina estará disponível em breve.')
print('Aguarde as próximas informações.')
else:
print('A vacinação só ocorrerá daqui a 3 meses.')
print('Informe-se novamente neste prazo.')
print('Cuide da saúde sempre. Até a próxima.')
```

Perceba que a indentação precisa ser ajustada, uma vez que o último else é relativo ao `elif`. Por isso, eles precisam estar alinhados.

## ESTRUTURA DE REPETIÇÃO `FOR`
A **estrutura de repetição** `for` tem funcionamento muito semelhante nas linguagens **C** e **Python**. Porém, a sintaxe é diferente nas duas linguagens. Além disso, em Python existe maior flexibilidade, já que a repetição pode ser controlada por uma variável não numérica.

Antes de detalhar o for, vamos conhecer uma função de Python que gera uma lista de valores numéricos. Essa lista ajudará a verificar a repetição e deixará mais claro o entendimento do laço.

### As listas do tipo `range()`
Ao chamar o método `range()`, Python cria uma sequência de números inteiros, de maneira simples à mais complexa. Veja a seguir:

#### Simples
Ela pode ser chamada de maneira **simples**, apenas com um argumento. Nesse caso, a sequência começará em `0` e será incrementada de uma unidade até o limite do parâmetro passado (exclusive).

Por exemplo: `range(3)` cria a sequência (`0, 1, 2`).

#### Não iniciadas em 0
Para que a sequência não comece em 0, podemos informar o início e o fim como parâmetros, lembrando que o parâmetro fim não entra na lista (exclusive o fim). O padrão é incrementar cada termo em uma unidade. Ou seja, a chamada `range(2, 7)` cria a sequência (`2, 3, 4, 5, 6`).

#### Indicando início, fim e passo
Também é possível criar sequências mais complexas, indicando os parâmetros de início, fim e passo, nessa ordem. O passo é o valor que será incrementado de um termo para o próximo.

Por exemplo, `range(2, 9, 3)` cria a sequência (`2, 5, 8`).

### A sintaxe da estrutura `for`
A estrutura `for` tem a seguinte sintaxe em Python:

```python
1 for in :
2 Bloco que será repetido para todos os itens da sequência
3 Instrução fora do for
```

Cabe ressaltar a diferença de sintaxe entre as linguagens **C** e **Python**. Veja a Tabela 4:


Python
C


for in :
for (inicialização; condição; incremento ou decremento){


Instruções com 4 espaços de indentação
Bloco de instruções a ser repetido. A indentação não é exigida


Instrução fora do for
}

Vamos analisar um exemplo simples em Python: **imprimir todos os elementos de uma sequência criada com a chamada `range()`**. Veja uma possível implementação desse exemplo na Figura 2:

```python
for item in range(2, 9, 3):
print(item)
```

- A **linha 1** mostra a criação do laço, com a variável item percorrendo a sequência (`2, 5, 8`), criada pela chamada `range(2, 9, 3)`;
- A **linha 2** indica a instrução que será executada para cada repetição deste laço. O laço for executa a instrução da linha 2 três vezes, uma para cada elemento da sequência (2, 5, 8); o resultado está exemplificado na Tabela 5.



sequência
2
5
8


Iteração 1 do laço
item =
2
 
 


Iteração 2 do laço
item =
 
5
 


Iteração 3 do laço
item =
 
 
8

## O laço `for` com uma string
Python também permite que a repetição aconteça ao longo de uma string. Para isso, basta lembrar que a string é uma sequência de caracteres individuais. Suponha que você queira soletrar o nome informado pelo usuário. Uma possível implementação está na Figura 3:

```python
nome = input("Entre com seu nome: ")
for letra in nome:
print(letra)
```

- A **linha 1** faz com que a palavra inserida pelo usuário seja armazenada na variável nome;
- A **linha 2** mostra a criação do **laço**, com a variável letra percorrendo a sequência de caracteres armazenada na variável nome;
- A **linha 3** indica a instrução que será executada para cada repetição desse laço. O laço `for` executa a instrução da linha 3 tantas vezes quantos forem os elementos da sequência que está na variável nome.


1 Entre com o seu nome: Laura
2 L
3 a
4 u
5 r
6 a

# Uso do laço `for` com qualquer sequência
Até agora, estudamos o uso do laço `for` com **iterações** sobre **strings** e sobre **sequências numéricas**, mas **Python** permite ainda mais que isso!

Podemos utilizar o laço for com iterações sobre qualquer sequência, não somente as numéricas e as strings.


Observe o exemplo da Figura 5:

```python
nomes = ['Laura', 'Lis', 'Guilherme', 'Enzo', 'Arthur']
for nome in nomes:
print(nome)
```

Veja o resultado da execução na Figura 6:


1 Laura
2 Lis
3 Guilherme
4 Enzo
5 Arthur


## ESTRUTURA DE REPETIÇÃO `WHILE`
A estrutura de repetição `while` tem funcionamento e sintaxe muito semelhantes nas linguagens **C** e **Python**. Observe a comparação entre as duas linguagens na Tabela 6:

Python
C

while :
while {

Instruções com 4 espaços de indentação
Bloco de instruções a ser repetido. A indentação não é exigida

Instrução fora do while
}

Como exemplo inicial do uso do laço `while`, vamos analisar um programa em que o usuário precisa digitar a palavra “sair” para que o laço `while` seja encerrado.

Uma possível implementação desse exemplo em Python está na Figura 7:

```python
palavra = input('Entre com uma palavra: ')
while palavra != 'sair':
palavra = input('Digite sair para encerrar o laço: ')
print('Você digitou sair e agora está fora do laço')
```

- A **linha 1** representa a solicitação ao usuário para que ele insira uma palavra, que será armazenada na variável palavra;
- A **linha 2** cria o laço `while`, que depende da condição ;
- A **linha 3** será repetida enquanto a condição for verdadeira, ou seja, enquanto o valor da variável palavra for diferente de ‘sair’. Quando esses valores forem iguais, a condição do laço `while` será falsa e o laço será encerrado;
- A **linha 4** representa a impressão da mensagem fora do laço `while`.

Veja uma execução desse programa na Figura 8:


1 Entre com uma palavra: teste
2 Digite sair para encerrar o laço: Oi?
3 Digite sair para encerrar o laço: Estou tentando...
4 Digite sair para encerrar o laço: Aff...
5 Digite sair para encerrar o laço: Blz, entendi...
6 Digite sair para encerrar o laço: Sair
7 Digite sair para encerrar o laço: Ah! o 'S' foi maiúsculo
8 Digite sair para encerrar o laço: sair
9 Você digitou sair e agora está fora do laço


Observe agora outra execução do mesmo programa na Figura 9:


1 Entre com uma palavra: sair
2 Você digitou sair e agora está fora do laço


Perceba que ao digitar ‘sair’ logo na primeira solicitação, a linha 3 do nosso programa não é executada nenhuma vez. Ou seja, o programa nem chega a entrar no laço `while`.

Em **C**, existe outra estrutura muito semelhante ao `while`, chamada `do-while`. A diferença básica entre elas é o momento em que a condição é testada, como vemos a seguir:



No laço while, a condição é testada antes da iteração.
O laço while testa e executa caso a condição seja verdadeira.
No laço do-while, a condição é testada após a iteração.
O laço do-while executa e testa.
X
No laço do-while, a condição é testada após a iteração. O laço do-while executa e testa.

Infelizmente, a estrutura `do-while` não existe em Python. Isso não chega a ser um grande problema, porque podemos adaptar nosso programa e controlar as repetições com o laço `while`.

## O laço `while` infinito
**Laços infinitos** são úteis quando queremos executar um bloco de instruções indefinidamente.

O laço `while` infinito tem o seguinte formato:

```python
while True:
Bloco que será repetido indefinidamente
```

**Exemplo**: Suponha que você deseje criar uma aplicação que permaneça por meses ou anos sendo executada, registrando a temperatura ou a umidade de um ambiente. Logicamente, estamos supondo que você tenha essa informação disponível a partir da leitura de algum sensor. Deve-se tomar cuidado e ter certeza de que seu uso é realmente necessário para evitar problemas de consumo excessivo de memória.

# AS INSTRUÇÕES AUXILIARES `BREAK`, `CONTINUE` E `PASS`

## A instrução `break`
A instrução `break` funciona da mesma maneira em **C** e em **Python**. Ela interrompe as repetições dos laços `for` e `while`. Quando a execução do programa chega a uma instrução `break`, a repetição é encerrada e o fluxo do programa segue a partir da primeira instrução seguinte ao laço.

Para exemplificar o uso da instrução `break`, vamos voltar ao primeiro exemplo do laço `while`, utilizando o laço infinito. O laço será encerrado quando o usuário inserir a palavra ‘sair’. Veja a Figura 10:

```python
while True:
palavra = input('Entre com uma palavra: ')
if palavra == 'sair':
break
print('Você digitou sair e agora está fora do laço')
```

Caso haja **vários laços aninhados**, o `break` será relativo ao laço em que estiver inserido. Veja a Figura 11:

```python
while True:
print('Você está no primeiro laço.')
opcao1 = input('Deseja sair dele? Digite SIM para isso. ')
if opcao1 == 'SIM':
break # este break é do primeiro laço
else:
while True:
print('Você está no segundo laço.')
opcao2 = input('Deseja sair dele? Digite SIM para isso. ')
if opcao2 == 'SIM':
break # este break é do segundo laço
print('Você saiu do segundo laço.')
print('Você saiu do primeiro laço')
```

## A instrução `continue`
A instrução `continue` também funciona da mesma maneira em **C** e em **Python**. Ela atua sobre as repetições dos laços `for` e `while`, como a instrução `break`, mas não interrompe todas as repetições do laço. A instrução `continue` **interrompe apenas a iteração corrente**, fazendo com que o **laço passe para a próxima iteração**.

O exemplo a seguir imprime todos os números inteiros de `1` até `10`, pulando apenas o `5`. Veja sua implementação na Figura 12:

```python
for num in range(1, 11):
if num == 5:
continue
else:
print(num)
print('Laço encerrado')
```

Para ressaltar a diferença entre as instruções `break` e `continue`, vamos alterar a linha 3 do nosso programa, trocando a instrução `continue` pela instrução `break`. Veja a nova execução na Figura 13:


1
2
3
4
Laço encerrado


## A INSTRUÇÃO `PASS`
A instrução `pass` atua sobre a estrutura `if`, permitindo que ela seja escrita sem outras instruções a serem executadas caso a condição seja verdadeira. Assim, podemos concentrar as instruções no caso em que a condição seja falsa. Suponha que queiramos imprimir somente os números ímpares entre `1` e `10`. Uma implementação possível está na Figura 14:

```python
for num in range(1, 11):
if num % 2 == 0:
pass
else:
print(num)
print('Laço encerrado')
```

Veja a execução desse programa na Figura 15:


1
3
5
7
9
Laço encerrado

Claramente, seria possível reescrever a condição do `if-else` para que pudéssemos transformá-lo em um `if` simples, sem `else`. Porém, o objetivo aqui é mostrar o uso da instrução `pass`.

Agora que já vimos os principais conceitos relativos às estruturas de decisão e de repetição, vamos testar seus conhecimentos.

### 1. Considere o seguinte trecho de um programa escrito em Python:

```python
s = 0
for i in range(5):
s += 3*i
print(s)
```
Parabéns! A alternativa "C" está correta.

O laço for vai ser repetido 5 vezes, já que range(5) retorna a sequência (`0, 1, 2, 3, 4`). Vale observar que a instrução `print(s)` está fora do laço `for`, o que a leva a ser executada apenas uma vez quando o laço se encerrar. Isso elimina as opções A e B. A variável s começa com valor zero e é acrescida, a cada iteração, do valor `3*i`, sendo que `i` pertence à sequência (`0, 1, 2, 3, 4`). Ou seja, s recebe os acréscimos: `0 + 3 + 6 + 9 + 12`. Assim, ela termina o laço com o valor `30`, que será impresso pela instrução `print(s)`.

### 2. Considere o seguinte trecho de um programa escrito em Python:
```python
s = 0
a = 1
while s < 5:
s = 3*a
a += 1
print(s)
```

Parabéns! A alternativa "B" está correta.

Ao ser testada pela primeira vez, a condição do while é verdadeira, já que s vale zero. Assim, a variável s recebe o valor 3 (3x1) e a variável a é acrescida de uma unidade, ficando com o valor 2. Em seguida, é impresso o valor de s (3). A condição do while é, então, testada novamente, sendo mais uma vez verdadeira, porque s tem o valor 3 (menor que 5). Nessa iteração, a variável s recebe o valor 6 (3x2) e a variável a é acrescida de uma unidade, ficando com o valor 3. Em seguida, é impresso o valor de s (6). A condição do while é, então, testada novamente e é falsa, já que s tem o valor 6, maior que 5. Com isso, o laço while é encerrado e nada mais é impresso. Logo, foram impressos os valores 3 e 6.

# [Python] Subprogramas


Os **subprogramas** são elementos fundamentais dos programas e por isso são importantes no estudo de linguagens de programação. Neste módulo, abordaremos os conceitos de subprogramas, como características gerais, passagem de parâmetros e recursividade, além da utilização de subprogramas em Python.

Todos os subprogramas estudados neste módulo, com base em Sebesta (2018), têm as seguintes características:

- Cada subprograma tem **um único ponto de entrada**.
- A unidade de programa chamadora é suspensa durante a execução do subprograma chamado, o que significa que existe apenas um subprograma em execução em determinado momento.
- Quando a execução do subprograma termina, o controle sempre retorna para o chamador.

As definições básicas, conforme Sebesta (2018), estabelecem que:

1. Um subprograma é definido quando o desenvolvedor descreve a **interface** e as **ações da abstração** desse subprograma.
2. O subprograma foi chamado quando uma instrução traz um **pedido explícito para sua execução**.
3. O subprograma está ativo após o **início de sua execução**, a partir da sua chamada e enquanto ele não foi concluído.

O cabeçalho do subprograma é a primeira parte da definição, em que podem ser especificados o **nome**, os **parâmetros** e o **tipo de retorno do subprograma**.

Em **C**, o cabeçalho dos subprogramas – sendo chamados de funções – traz, em ordem: o **tipo de retorno**, o **nome** e a **lista de parâmetros**, como a seguir:

```c
float calculaIMC (int peso, float altura)
```

Em Python, as funções definidas pelo desenvolvedor devem ser precedidas pela palavra reservada `def`. Não são especificados o tipo de retorno nem os tipos dos parâmetros, como no exemplo a seguir:

```python
def calculaIMC (peso, altura)
```

Em Python, as sentenças de função `def` são executáveis. Isso implica que a função só pode ser chamada após a execução da sentença `def`. Veja o exemplo na Figura 16:

```python
escolha = input("Escolha uma opção de função: 1 ou 2")
if escolha == 1:
def func1(x):
return x + 1
else:
def func2(x):
return x + 2

s = func1(10)
print(s)
```

A função `func1()` só pode ser chamada caso a variável escolha seja igual a `1`. Ou seja, o usuário deverá inserir `1` quando solicitado (na linha 1), para que a linha 9 possa ser executada sem que seja gerado um erro.

Usualmente, um subprograma executa cálculos e operações a partir de dados que ele deve processar. Existem duas maneiras de o subprograma obter esses dados: acessando variáveis não locais, mas visíveis para o subprograma, ou pela passagem de parâmetros.

Quando o subprograma recebe os parâmetros adequados, ele pode ser executado com quaisquer valores recebidos. Porém, quando ele manipula variáveis não locais, uma forma de evitar alterações indevidas nessas variáveis é fazendo cópias locais delas. De acordo com Sebesta (2018), o acesso sistemático a variáveis não locais pode diminuir a confiabilidade do programa.

São denominados parâmetros formais aqueles do cabeçalho do subprograma.

Quando o subprograma é chamado, é necessário escrever o nome do subprograma e a lista de parâmetros a serem vinculados aos parâmetros formais dele, que são denominados parâmetros reais ou argumentos.

No exemplo da Figura 16, existe o cabeçalho da função func1 na linha 3, com o parâmetro formal x. Na linha 9, a função func1 é chamada com o parâmetro real 10.

Em Python, é possível estabelecer valores padrão para os parâmetros formais. O valor padrão é usado quando a chamada da função ocorre sem nenhum parâmetro real. Veja o exemplo de definição e chamada da função taxímetro na Figura 17:

```python
def taximetro(distancia, multiplicador=1):
largada = 3
km_rodado = 2
valor = (largada + distancia * km_rodado) * multiplicador
return valor

pagamento = taximetro(3.5)
print(pagamento)
```

Observe que mesmo com a definição da linha 1 de dois parâmetros formais, a chamada da função na linha 8 ocorre apenas com um parâmetro real.

A palavra reservada `return` indica que a função **retorna** algum valor. Isso implica que o valor retornado seja armazenado em uma variável do programa chamador (como ocorre na linha 8), ou utilizado como parâmetro para outra função.

> **Atenção!** Retornar um valor é diferente de imprimir na tela. Ao utilizar a função `print()`, ocorre apenas a impressão de algo na tela, o que não significa que tenha havido retorno de qualquer função definida pelo usuário.

## PROCEDIMENTOS E FUNÇÕES
Os **subprogramas** podem ser, distintamente, **procedimentos** e **funções**. De acordo com Sebesta (2018):


Procedimentos
Funções


São aqueles que não retornam valores.
São aquelas que retornam valores.

Na maioria das linguagens que não explicita a diferença entre eles, as funções podem ser definidas sem retornar qualquer valor, tendo comportamento de procedimento. Esse é o caso de Python. Veja o exemplo da Figura 18:

```python
def func1(x):
x = 10
print(f'Função func1 - x = {x}')

def func2(x):
x = 20
print(f'Função func2 - x = {x}')

x = 0
func1(x)
func2(x)
print(f'Programa principal - x = {x}')
```

As funções `func1(x)` e `func2(x)` não possuem qualquer retorno. Ou seja, são funções com **comportamento de procedimentos**.

## AMBIENTES DE REFERENCIAMENTO LOCAL

### Variáveis locais
Quando um subprograma define suas **próprias variáveis**, estabelece ambientes de referenciamento local. Essas variáveis são chamadas de **variáveis locais**, com seu escopo usualmente sendo o corpo do subprograma. As variáveis locais podem ser:

#### Dinâmicas da pilha
São vinculadas ao armazenamento no início da execução do subprograma e desvinculadas quando essa execução termina. As variáveis locais dinâmicas da pilha têm diversas vantagens, e a principal delas é a **flexibilidade**. Suas principais desvantagens são o **custo do tempo** – para alocar, **inicializar** (quando necessário) e **liberar tais variáveis para cada chamada ao subprograma** – e o fato de que os **acessos a essas variáveis locais devem ser indiretos**, enquanto os acessos às variáveis estáticas podem ser diretos.

> **Atenção!** Nas linguagens **C** e **C++**, as **variáveis locais** são _dinâmicas da pilha_, a menos que sejam especificamente declaradas como **static**. **Todas as variáveis locais em Python são dinâmicas da pilha**. As **variáveis globais** são declaradas em definições de método, e qualquer variável declarada global em um método precisa ser definida fora dele. Caso haja uma atribuição à variável local com mesmo nome de uma variável global, esta é implicitamente declarada como local.

Voltando ao exemplo da Figura 18, vamos detalhar as funções func1(x) e func2(x):

- As linhas 1, 2 e 3 definem a função `func1(x)`, que recebe o parâmetro `x`, mas tem uma variável local de nome `x`, cujo valor atribuído é `10`;
- Analogamente, a função `func2(x)` – definida nas linhas 6, 7 e 8 – que recebe o parâmetro `x` e tem uma variável de mesmo nome, mas com valor atribuído `20`;
- O programa principal tem uma variável global de mesmo nome `x`, cujo valor atribuído é `0`, na linha 11;
- Veja que as chamadas às funções `func1(x)` e `func2(x)` ocorrem nas linhas 12 e 13, quando a variável `x` global já recebeu o valor `0`. Porém, ao serem executadas, cada uma dessas funções tem a sua própria variável local, a quem todas as referências internas são feitas.

Confira a execução desse exemplo na Figura 19:

```python
Função func1 - x = 10

Função func2 - x = 20

Programa principal - x = 0
```

Mesmo com a variável global tendo valor nulo, cada variável local das funções `func1(x)` e `func2(x)` tem seu próprio valor, e não ocorrem alterações na variável global mesmo com as atribuições das linhas 2 e 7.

Para alterar a variável global `x`, seria necessário explicitar dentro de cada função que o nome `x` é referente a ela. Isso pode ser feito com a palavra reservada global. Além de explicitar a referência à variável global, as funções `func1(x)` e `func2(x)` não recebem mais os parâmetros de mesmo nome, já que fazem referência à variável global. Veja como ficaria o nosso exemplo com essa pequena alteração na Figura 20:

```python
def func1():
global x
x = 10
print(f'Função func1 - x = {x}')

def func2():
global x
x = 20
print(f'Função func2 - x = {x}')

13 x = 0
14 func1()
15 func2()
12 print(f'Programa principal - x = {x}')
```

Observe agora a execução desse exemplo alterado na Figura 21:

```python
Função func1 - x = 10
Função func2 - x = 20
Programa principal - x = 20
```

Percebe-se que o `print()` do programa principal está na linha 16, depois da chamada à função `func2(x)`. Dessa forma, a variável global `x` foi alterada na execução da `func2(x)` e fica com o valor `20` quando a execução volta ao programa principal.

## Subprogramas aninhados
Em Python, e na maioria das linguagens funcionais, é permitido aninhar subprogramas. Porém, as linguagens **C** e **C++** não permitem essa prática. Veja o exemplo da Figura 22:

```python
def func1():
global x
x = 10
print(f'Função func1 - x = {x}')


def func2():
global x
x = 20
print(f'Função func2 - x = {x}')


x = 0
func1()
func2()
print(f'Programa principal - x = {x}')
```

Observe agora a execução desse exemplo alterado na Figura 21:

```python
Função func1 - x = 10
Função func2 - x = 20
Programa principal - x = 20
```

Percebe-se que o `print()` do programa principal está na linha 16, depois da chamada à função `func2(x)`. Dessa forma, a variável global `x` foi alterada na execução da `func2(x)` e fica com o valor `20` quando a execução volta ao programa principal.

## Subprogramas aninhados
Em Python, e na maioria das linguagens funcionais, é permitido aninhar subprogramas. Porém, as linguagens **C** e **C++** não permitem essa prática. Veja o exemplo da Figura 22:

```python
def taximetro(distancia)::
def calculaMult():
if distancia < 5:
return 1.2
else:
return 1
multiplicador = calculaMult()
largada = 3
km_rodado = 2
valor = (largada + distancia * km_rodado) * multiplicador
return valor


dist = eval(input("Entre com a distancia a ser percorrida em km: "))
pagamento = taximetro(dist)
print(f'O valor a pagar é R$ {pagamento}')
```

A função `taximetro()` tem, dentro de sua definição, a definição de outra função denominada `calculaMult()`. Na linha 7, a função `calculaMult()` é chamada e o seu retorno é armazenado na variável **multiplicador**.

## MÉTODOS DE PASSAGENS DE PARÂMETROS
Os **métodos de passagem de parâmetros** são as maneiras que existem para transmiti-los ou recebê-los dos subprogramas chamados. Os parâmetros podem ser passados principalmente por:

### Valor
O **parâmetro formal** funciona como uma variável local do subprograma, sendo inicializado com o valor do parâmetro real. Dessa maneira, não ocorre alteração na variável externa ao subprograma, caso ela seja passada como parâmetro.

### Referência
Em vez de passar o valor do **parâmetro real**, é transmitido um caminho de acesso (normalmente um endereço) para o subprograma chamado. Isso fornece o caminho de acesso para a célula que armazena o parâmetro real. Assim, o subprograma chamado pode acessar o parâmetro real na unidade de programa chamadora.

### Saiba mais +
Na **linguagem C**, utilizamos ponteiros para fazer a passagem de parâmetros por referência. As **transmissões de parâmetros** que não sejam ponteiros utilizam a passagem por valor.

O método de passagem de parâmetros de Python é chamado **passagem por atribuição**. Como todos os valores de dados são objetos, toda variável é uma referência para um objeto. Ao se estudar orientação a objetos, fica mais clara a diferença entre a passagem por atribuição e a passagem por referência. **Por enquanto, podemos entender que a passagem por atribuição é uma passagem por referência, pois os valores de todos os parâmetros reais são referências**.

## RECURSIVIDADE
Uma **função recursiva** é aquela que chama a si mesma. Veja o exemplo da função `regressiva()`, como mostrado na Figura 23:

```python
def regressiva(x):
print(x)
regressiva(x - 1)
```
Na implementação da função `regressiva()`, tendo `x` como parâmetro, ela própria é chamada com o parâmetro `x – 1`. Vamos analisar a chamada regressiva(2):

1. Ao chamar regressiva(2), o valor 2 é exibido na tela pela linha 2, e ocorre uma nova chamada da função regressiva() na linha 3, com o parâmetro 1. Vamos continuar com esse caminho de execução da regressiva(1).

2. Ao chamar regressiva(1), o valor 1 é exibido na tela pela linha 2, e ocorre uma nova chamada da função regressiva() na linha 3, com o parâmetro 0.

3. Ao chamar regressiva(1), o valor 1 é exibido na tela pela linha 2, e ocorre uma nova chamada da função regressiva() na linha 3, com o parâmetro 0.

> **Atenção!** Conceitualmente, essa execução será repetida indefinidamente até que haja algum erro por falta de memória. Perceba que não definimos adequadamente uma condição de parada para a função `regressiva()`, o que leva a esse comportamento ruim.

Em Python, o interpretador pode interromper a execução indefinida, mas essa não é uma boa prática. Uma forma de contornar esse problema é definir adequadamente uma condição de parada, como no exemplo da Figura 24:

```python
def regressiva(x):
if x <= 0:
print("Acabou")
else:
print(x)
regressiva(x-1)
```

Uma **função recursiva** que termina tem:

- Um ou mais casos básicos, que funcionam como condição de parada da recursão.

- Uma ou mais chamadas recursivas, que têm como parâmetros valores mais próximos do(s) caso(s) básico(s) do que o ponto de entrada da função.

Alguns exemplos clássicos de funções que podem ser implementadas de forma recursiva são **o cálculo do fatorial de um inteiro não negativo** e a **sequência de Fibonacci**, que serão explorados a seguir.

### A função recursiva fatorial
A `função matemática fatorial de um inteiro não negativo n` é calculada por:

![função recursiva fatorial](https://user-images.githubusercontent.com/61624336/138567412-761c5bad-3216-4e3e-a59a-99a7706c8343.png)

Além disso, ela pode ser definida recursivamente por:

![função recursiva fatorial](https://user-images.githubusercontent.com/61624336/138567565-ae25867c-7f2a-4236-bb05-90b0c2280896.png)

Uma implementação recursiva da função fatorial em Python está na Figura 25:

```python
def fatorial(n):
if n == 0 or n == 1:
return 1
else:
return n*fatorial(n-1)
```

Vale ressaltar que a função fatorial também poderia ter sido implementada de forma **não recursiva**, como mostrado na Figura 26:

```python
def fatorial(n):
fat = 1
if n == 0 or n == 1:
return fat
else:
for x in range(2, n + 1):
fat = fat*x
return fat
```

Porém, neste tópico, o intuito principal é explorar a recursividade.

### A sequência de Fibonacci
A **sequência de Fibonacci** é: `1, 1, 2, 3, 5, 8, 13, 21...` Os dois primeiros termos são `1` e, a partir do 3º termo, cada termo é a soma dos dois anteriores.

Uma possível implementação recursiva de função que determina o **n-ésimo** termo da sequência de Fibonacci está na Figura 27:

```python
def fibo(n):
if n == 1 or n == 2:
return 1
else:
return fibo(n - 1) + fibo(n - 2)
```

- A **linha 2** traz as condições de parada.
- A **linha 5** traz as chamadas recursivas para calcular os dois termos anteriores da sequência.

## DOCSTRINGS
**Em Python, é possível definir uma string que serve como documentação de funções definidas pelo desenvolvedor**. Ao chamar o utilitário `help()` passando como parâmetro a função desejada, essa string é exibida. Veja a Figura 28 e a Figura 29:

```python
def fibo(n):
'Determina o n-ésimo termo da sequência de Fibonacci'
if n == 1 or n == 2:
return 1
else:
return fibo(n - 1) + fibo(n - 2)

print(help(fibo))
```

~~~python
fibo(n)
Determina o n-ésimo termo da sequência de Fibonacci
~~~

- Na Figura 28, a **linha 2** mostra a declaração da docstring.
- A **linha 8** mostra a impressão na tela da chamada `help(fibo)`. Na Figura 29, está o resultado da execução desse programa.

Python oferece, em seu núcleo, algumas funções que já utilizamos, como `print()` e `input()`, além de classes como `int`, `float` e `str`. Logicamente, o núcleo da linguagem Python disponibiliza muitas outras funções (ou métodos) e classes além das citadas. Mas, ainda assim, ele é pequeno, com objetivo de simplificar o uso e ganhar eficiência. Para aumentar a disponibilidade de funções, métodos e classes, o desenvolvedor pode usar a biblioteca padrão Python. Apresentaremos alguns dos principais recursos dessa biblioteca e a forma de utilizá-los.

# 🐍 [Python] Getting started

Existem várias maneiras de obter a sua própria cópia do Python 3, dependendo do sistema operativo que utilize.

Utilizadores de **Linux** provavelmente já têm o Python instalado - este é o cenário mais provável, já que a infraestrutura do Python é intensamente utilizada por muitos componentes do sistema operativo Linux.

Por exemplo, alguns distribuidores podem acoplar as suas ferramentas específicas ao sistema e muitas destas ferramentas, como gestores de pacotes, são frequentemente escritas em Python. Algumas partes de ambientes gráficos disponíveis no mundo Linux também podem utilizar o Python.

Se for um utilizador Linux, abra o terminal/console e digite:

[![bash](https://img.shields.io/badge/-bash-4EAA25?style=social&logo=GNU-Bash&logoColor=000000)](#)

```sh
python3
```

no shell prompt, pressione Enter e aguarde. Se vir algo deste gênero:


Python 3.4.5 (default, Jan 12 2017, 02:28:40)
[GCC 4.2.1 Compatible Clang 3.7.1 (tags/RELEASE_371/final)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Se o Python 3 estiver ausente, consulte a sua documentação do Linux para saber como utilizar o seu gestor de pacotes para descarregar e instalar um novo pacote - o que precisa chama-se python3, ou o seu nome começa com isso.

> Todos os utilizadores que não sejam Linux podem descarregar uma cópia em: https://www.python.org/downloads/.

> **Nota**: Por padrão, a versão do Python 2 já se encontra instalado nas máquinas do sistema operacional Linux e macOS.

Como o browser diz ao site onde entrou o sistema operativo que utiliza, o único passo que tem de dar é clicar na versão Python apropriada que deseja.

Neste caso, selecione Python 3. O site oferece sempre a versão mais recente do mesmo. Se for um utilizador do Windows, inicie o arquivo `.exe` descarregado e siga todos os passos.

> **Windows Env**: Caso seja um usuário de Windows, deixe as configurações padrão que o instalador sugere por agora, com uma exceção - veja a caixa de verificação chamada `Add Python 3.x to PATH` e verifique-a. Isto tornará as coisas mais fáceis, pois vai adicionar o caminho do python3 instalado na sua máquina local para as variáveis de ambiente do seu sistema operacional Windows.

Se for um utilizador MacOS, uma versão do Python 2 pode já ter sido pré-instalada no seu computador, mas como vamos trabalhar com o Python 3, ainda assim terá de descarregar e instalar o arquivo `.pkg` relevante a partir do site Python.

Agora que tem o Python 3 instalado, é altura de verificar se funciona, e fazer o primeiro uso do mesmo. Este será um procedimento muito simples, mas deve ser o suficiente para o convencer de que o ambiente Python é completo e funcional.

Se ao usar o Python você sentir que o gerenciador de pacotes não está funcionando corretamente, e apresentar esse log: `Python :Fatal error in launcher: Unable to create process using ""C:\Program Files (x86)(5solution)`, clique no link para aprender como resolver.

Caso se você estiver utilizando uma versão mais antiga e deseja atualizar para uma versão mais atual, veja como no link.

Existem muitas formas de utilizar o Python, especialmente se vier a ser um programador Python. Para começar o seu trabalho, precisa das seguintes ferramentas:

- um **editor** que o irá apoiar na escrita do código (deve ter algumas características especiais, não disponíveis em ferramentas simples); este editor dedicado dar-lhe-á mais do que o equipamento padrão do sistema operativo;
- uma **console** na qual pode rodar o seu código recém-escrito e pará-lo à força quando ficar fora de controle;
- uma ferramenta chamada de **debugger**, capaz de rodar o seu código passo a passo e que lhe permite inspecioná-lo em cada momento da execução.

Para além dos seus muitos componentes úteis, a instalação padrão de Python 3 contém uma aplicação muito simples mas extremamente útil chamada "IDLE".

Com o **IDLE** - Integrated Development and Learning Environment iniciado. Isto é o que deve ver:

![7d79de4a3439191bc815d1d0d51dd6e8bd08bcf0](https://user-images.githubusercontent.com/61624336/195676458-cce0c851-f6b2-4e47-b85e-ecde19d44357.png)

IDLE é uma boa escolha para iniciantes em Python. É um ambiente de desenvolvimento fácil de aprender e usar, e fornece as ferramentas básicas necessárias para começar a programar em Python. O IDLE é um ambiente de desenvolvimento integrado (IDE) para a linguagem de programação Python. Ele é incluído na distribuição padrão do Python e está disponível para Windows, macOS e Linux. IDLE é um ambiente de desenvolvimento simples e fácil de usar. Ele fornece as ferramentas básicas necessárias para escrever, depurar e executar código Python. As principais características do IDLE incluem:

- Um editor de texto com recursos básicos, como autocompletar e realce de sintaxe.
- Um shell Python interativo que permite executar código Python linha por linha.
- Um depurador (debugger) que permite depurar código Python passo a passo.
- Um gerenciador de projetos que permite organizar arquivos Python.

Outra forma de executar os comandos da linguagem é por meio de **notebooks** que são ambientes interativos que permitem criar e compartilhar documentos que misturam código executável, texto explicativo, imagens, gráficos e outros elementos. Existem vários tipos de notebooks para Python que podem ser encontrados na internet, cada um com suas características, vantagens e desvantagens. Eles são muito populares na comunidade de ciência de dados, programação e pesquisa. Os notebooks mais conhecidos são os Jupyter Notebooks e Google Colab, que são documentos que podem conter tanto código (em várias linguagens, incluindo Python, R, Julia, entre outras) quanto elementos de texto formatado, imagens, equações matemáticas e visualizações. Eles são divididos em células, onde cada célula pode conter código para ser executado ou texto explicativo formatado usando Markdown. Isso permite uma exploração interativa de dados e uma maneira eficiente de documentar o código e seus resultados.

Os notebooks oferecem vantagens, como:

- Interatividade: Permite executar pequenos trechos de código de maneira independente em cada célula, visualizando imediatamente os resultados.

- Visualização de dados: É possível gerar gráficos, tabelas e visualizações diretamente no documento, facilitando a análise e compreensão dos dados.

- Documentação intercalada com código: Permite explicar o raciocínio por trás do código e dos resultados, facilitando a compreensão para outras pessoas que interagem com o notebook.

- Compartilhamento e colaboração: Os notebooks podem ser facilmente compartilhados com outras pessoas, permitindo a colaboração em projetos e a reprodução dos passos realizados.

Esses notebooks são utilizados em diversas áreas, incluindo ciência de dados, aprendizado de máquina, pesquisa acadêmica, análise exploratória de dados, entre outras, devido à sua flexibilidade e capacidade de integração de código e documentação.

[![Jupyter](https://img.shields.io/badge/-Jupyter-fff?style=social&logo=Jupyter&logoColor=orange)](https://jupyter.org/try)

O **Jupyter Notebook** é um aplicativo da web de código aberto que pode ser usado para construir e compartilhar código ativo, equações, visualizações e documentos de texto. O Jupyter Notebook é mantido pelo pessoal do Projeto Jupyter. Ele suporta mais de 40 linguagens de programação, incluindo Python, R, Julia e Scala. Ele também permite a integração com bibliotecas e frameworks populares de ciência de dados, como numpy, pandas, scikit-learn, tensorflow, etc.

[![Colab](https://img.shields.io/badge/-Colab-fff?style=social&logo=Google-Colab&logoColor=F9AB00)](https://colab.research.google.com/)

O **Google Colab**, ou Colaboratory, é uma plataforma baseada em nuvem fornecida pelo Google que permite escrever, compartilhar e executar códigos Python diretamente no navegador. É especialmente útil para análise de dados, aprendizado de máquina, educação em ciência de dados e para executar notebooks Jupyter. É um serviço de nuvem gratuito que oferece notebooks para Python que podem ser executados no navegador. O Google Colab permite o uso de GPUs e TPUs gratuitamente, o que é muito útil para treinar modelos de aprendizado de máquina complexos. Ele também facilita o compartilhamento e a colaboração de notebooks com outros usuários. Ele é baseado no Jupyter Notebook e tem uma interface semelhante.

Alguns dos principais recursos do Google Colab incluem:

- Ambiente de notebook interativo: Permite escrever e executar código Python em células individuais, facilitando a experimentação e a visualização dos resultados.

- Gratuito com recursos de GPU e TPU: Oferece acesso gratuito a recursos de hardware, como GPUs (Unidades de Processamento Gráfico) e TPUs (Unidades de Processamento Tensorial), o que é especialmente útil para tarefas intensivas de computação, como aprendizado de máquina e treinamento de modelos.

- Integração com o Google Drive: Permite importar conjuntos de dados e salvar resultados diretamente no Google Drive.

- Compartilhamento fácil: Os notebooks podem ser compartilhados com outras pessoas, permitindo colaboração em tempo real.

Essa plataforma é bastante utilizada por cientistas de dados, pesquisadores, desenvolvedores e estudantes devido à sua facilidade de acesso, recursos gratuitos e flexibilidade para executar códigos complexos em um ambiente baseado na nuvem.

[![Colab](https://img.shields.io/badge/-Colab-fff?style=social&logo=Google-Colab&logoColor=F9AB00)](https://colab.research.google.com/notebooks/snippets/importing_libraries.ipynb#scrollTo=GQ18Kd5F3uKe)

O colab já fornece um conjunto de bibliotecas python já instaladas em seus notebooks, porém caso aja uma excessão de biblioteca não instalada, recomendo seguir os passos que a própria plataforma indica, basta clicar no badge acima para aprender sobre os snippets de importação de bibliotecas do colab.

O **Kaggle** é uma plataforma online que hospeda competições de ciência de dados e aprendizado de máquina. Ele também oferece notebooks para Python que podem ser usados para explorar, analisar e modelar dados. O Kaggle permite o acesso a conjuntos de dados públicos e privados, bem como a GPUs e TPUs gratuitas. Ele também tem uma comunidade ativa de cientistas de dados e aprendizes de máquina que compartilham seus notebooks e soluções.

Veja como escrever e executar o seu primeiro programa:

É agora tempo de escrever e executar o seu primeiro programa de Python 3. Será muito simples, por agora.

O primeiro passo é criar um novo source file e preenchê-lo com código. Clique em `File` no menu do IDLE e escolha `New file`.

![9c47dcbd53615728044a921159aef565968d7f3c](https://user-images.githubusercontent.com/61624336/195677372-128a8986-dde7-4f69-983b-cd0bf0046f6a.png)

> Como pode ver, o IDLE abre uma nova janela para si. Pode utilizá-la para escrever e alterar o seu código.

Esta é a **janela do editor**. O seu único objetivo é ser um local de trabalho em que o seu source code é tratado. Não confundir a janela do editor com a janela shell. Desempenham funções diferentes.

A janela do editor está atualmente sem título, mas é uma boa prática começar a trabalhar nomeando o source file.

Clique em `File` (na nova janela), depois clique em `Save as...`, selecione uma pasta para o novo ficheiro (o ambiente de trabalho é um bom local para as suas primeiras tentativas de programação) e escolha um nome para o novo ficheiro.

![ed0d023d260245eecd1be0f4b0ff02fec660b9da](https://user-images.githubusercontent.com/61624336/195678750-3a44b5bd-0fd7-4a90-8c9b-bc7c35a482d3.png)

[![.py](https://img.shields.io/badge/-.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

> **Nota**: não defina nenhuma extensão para o nome do ficheiro que vai utilizar. O Python precisa que os seus ficheiros tenham a extensão `.py`, por isso deve confiar nas predefinições da janela de diálogo. A utilização da extensão padrão `.py` permite que o sistema operativo abra adequadamente estes ficheiros.

Agora coloque apenas uma linha na sua janela do editor recém-aberta e nomeada. A linha tem este aspeto:

[![.py](https://img.shields.io/badge/-snake.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hisssssss...")
```

> O comando `print()` , que é uma das diretivas mais fáceis em Python, imprime simplesmente uma linha para o ecrã.

Veja mais de perto as aspas. Estas são as formas mais simples de aspas (neutras, retas, mudas, etc.) tipicamente utilizadas nos source files. Não tente usar aspas tipográficas (curvas, curvilíneas, inteligentes, etc.), utilizadas por processadores de texto avançados, uma vez que o Python não as aceita.

![0830563fa18ea8138503c208eb9514af574d7a2c](https://user-images.githubusercontent.com/61624336/195683022-8105137e-d987-45f3-a003-407c320d1fa8.png)

Guarde o ficheiro `File > Save` e execute o programa `Run -> Run Module`.

Se tudo correr bem e não houver erros no código, a janela do console irá mostrar-lhe os efeitos causados pela execução do programa. Neste caso, o programa sibila. Tente executá-lo mais uma vez. E mais uma vez. Agora feche ambas as janelas e regresse ao ambiente de trabalho.

![0ced7f0e762ae8260831e994370b1ff2b8b7fd7b](https://user-images.githubusercontent.com/61624336/195683486-5c9343e2-37ab-48dd-a3af-32fe8d7c5905.png)

Aprenda como estragar e corrigir o seu código! Agora, reinicie o IDLE. Clique em `File > Open > aponte para o ficheiro que guardou anteriormente e deixe o IDLE lê-lo`.

Tente executá-lo novamente pressionando `F5` quando a janela do editor estiver ativa. Como pode ver, o IDLE é capaz de guardar o seu código e recuperá-lo quando precisar dele novamente.

O IDLE contém um recurso adicional e útil.

1. Primeiro, remova o parêntesis final.
2. Em seguida, insira o parêntesis novamente.

O seu código deve parecer-se com o que está aqui em baixo:

[![.py](https://img.shields.io/badge/-snake.py_(output)-fff?style=social&logo=Python&logoColor=3776AB)](#)

Hisssssss...

![ed47c4a8c77b4dd27800cb500f5f412c1fcb12fd](https://user-images.githubusercontent.com/61624336/195691376-2ca185ce-87f3-4b37-ab0d-304df28154d3.png)

Cada vez que colocar o parêntesis final no seu programa, o IDLE mostrará a parte do texto limitada com um par de parêntesis correspondentes. Isto ajuda-o a lembrar-se de os colocar em pares.

Retire novamente o parêntesis final. O código torna-se incorreto. Contém agora um erro de sintaxe. O IDLE não deve deixar que o execute.

Tente executar o programa novamente. O IDLE irá lembrá-lo de guardar o ficheiro modificado. Siga as instruções.

Observe cuidadosamente todas as janelas. Uma nova janela – diz que o intérprete encontrou um EOF (end-of-file) embora (na sua opinião) o código deva conter mais algum texto.

A janela do editor mostra claramente onde isto aconteceu.

![112b321a4d7620c67b0e037f8861fd71a9cb09df](https://user-images.githubusercontent.com/61624336/195692443-b541e9c2-702c-4e28-89c7-5ceb1b6d22ae.png)

Corrija o código agora. Deve ficar assim:

[![.py](https://img.shields.io/badge/-snake.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hisssssss...")
```

Execute-o para ver se “sibila” novamente.

Vamos estragar o código mais uma vez. Remova uma letra da palavra `print`. Execute o código pressionando `F5`. O que acontece agora?

![aa281b654b986dbe066685abaa0dcf8a3b842705](https://user-images.githubusercontent.com/61624336/195692660-b70902e1-f4c6-4990-a179-e6c6c8147312.png)

Deve ter notado que a mensagem de erro gerada para o erro anterior é bastante diferente da primeira.

![9e63a9fdc2ed8afae211381c57ccd02967eb4ebc](https://user-images.githubusercontent.com/61624336/195699490-9083ed72-8712-4793-ab8b-8092671ad63b.png)

Isto acontece porque a natureza do erro é diferente e o erro é descoberto numa fase diferente de interpretação.

A janela do editor não fornecerá qualquer informação útil sobre o erro, mas as janelas da console poderão.

A mensagem (a vermelho) mostra (nas linhas subsequentes):

- o **traceback** (que é o caminho que o código percorre através de diferentes partes do programa - pode ignorá-lo por agora, uma vez que está vazio num código tão simples);
- a **localização do erro** (o nome do ficheiro contendo o erro, o número da linha e o nome do módulo);

> **Nota**: o número pode ser enganador, uma vez que o Python normalmente mostra o local onde primeiro se notam os efeitos do erro, não necessariamente o erro em si.

- o **conteúdo da linha errada**;

> **Nota**: a janela do editor IDLE não mostra os números das linhas, mas mostra a localização atual do cursor no canto inferior direito; use-a para localizar a linha errada num source code longo;

- o **nome do erro** e uma breve explicação.

> Experimente criar novos ficheiros e executar o seu código. Tente fazer output de uma mensagem diferente para o ecrã, por exemplo `roar!`, `meow`, ou até mesmo talvez um `oink!`. Tente estragar e corrigir o seu código - veja o que acontece.

Ao retirar as aspas do argumento da `string` também é gerado um erro. Veja mais:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print(Hisssssss...)
```
[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


Traceback (most recent call last):
File "snake.py", line 1, in
print(Hisssssss...)
NameError: name 'Hisssssss...' is not defined

Agora, ao retirar os parênteses da função com o argumento `string` também é gerado um erro. Veja mais:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
printHisssssss...
```
[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


Traceback (most recent call last):
File "snake.py", line 1, in
printHisssssss...
NameError: name 'printHisssssss...' is not defined

Agora, veja o que acontece ao colocar aspas duplas sem parênteses:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print"Hisssssss..."
```
[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


File "snake.py", line 1
print"Hisssssss..."
^
SyntaxError: invalid syntax

Agora, veja o que acontece ao colocar duas aspas distintas sem parênteses:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print'Hisssssss..."
```
[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


File "snake.py", line 1
print'Hisssssss..."
^
SyntaxError: EOL while scanning string literal

Agora, veja o que acontece ao colocar duas aspas simples ou duplas sem parênteses e com um sinal de igual:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print='Hisssssss...'
```
[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


Success (1.82s)

> Ele compila, mas não exibe resultado! Pois ele identificou como uma **variável** armazenando um valor, mas se exibirmos essa variável vai existir um erro de tipo.

## [Python] `Hello, World!`

É tempo de começar a escrever algum código Python real e funcional. Vai ser muito simples por enquanto.

Como vamos mostrar-lhe alguns conceitos e termos fundamentais, estes snippets de código não serão sérios ou complexos.

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hello, World!")
```

Execute o código na janela do editor à direita. Se tudo correr bem aqui, verá a linha de texto na janela da console.

Em alternativa, lançe o IDLE, crie um novo source file Python, preencha-o com este código, nomeie o ficheiro e guarde-o. Agora execute-o. Se tudo correr bem, verá o texto contido dentro das aspas na janela da console IDLE. O código que executou deve parecer familiar. Viu algo muito semelhante quando o conduzimos através da criação do ambiente IDLE.

Agora vamos passar algum tempo a mostrar e a explicar-lhe o que está realmente a ver, e porque é que se parece com isto.

Como pode ver, o primeiro programa consiste nas seguintes partes:

- a palavra `print`;
- um parêntesis de abertura;
- umas aspas;
- uma linha de texto: `Hello, World!`;
- outras aspas;
- um parêntesis de fecho.

Cada um dos itens acima desempenha um papel muito importante no código.

Veja a linha de código abaixo:

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hello, World!")
```

A palavra `print` que se pode ver aqui é um **nome de função**. Isso não significa que, onde quer que a palavra apareça, é sempre um nome de função. O significado da palavra vem do contexto em que a palavra foi usada.

Provavelmente já encontrou o termo função muitas vezes antes, durante as aulas de matemática. Provavelmente também pode listar vários nomes de funções matemáticas, como seno ou log.

As funções Python, no entanto, são mais flexíveis e podem conter mais conteúdo do que as suas irmãs matemáticas.

Uma função (neste contexto) é uma parte separada do código do computador capaz de:

- **causar um qualquer efeito** (por exemplo, enviar texto para o terminal, criar um ficheiro, desenhar uma imagem, reproduzir um som, etc.); isto é algo completamente inédito no mundo da matemática;
- **avaliar um valor** (por exemplo, a raiz quadrada de um valor ou o comprimento de um dado texto) e **devolvê-lo como o resultado da função**; é isto que faz as funções Python serem os parentes dos conceitos matemáticos.

Além disso, muitas das funções Python podem fazer as duas coisas acima juntamente.

De onde vêm as funções?

- Podem vir **do próprio Python**; a função `print` é uma deste tipo; tal função é um valor acrescentado recebido juntamente com o Python e o seu ambiente (é **incorporada**); não é necessário fazer nada de especial (por exemplo, perguntar a alguém por qualquer coisa) se quiser fazer uso dela;
- podem ser provenientes de um ou mais dos add-ons de Python chamados **módulos**; alguns dos módulos vêm com Python, outros podem requerer instalação separada - seja qual for o caso, todos eles precisam de estar explicitamente ligados ao seu código (mostrar-lhe-emos como fazê-lo em breve);
- pode **escrevê-los você mesmo**, colocando tantas funções quantas quiser e precisar dentro do seu programa para o tornar mais simples, mais claro e mais elegante.

O nome da função deve ser **significativo** (o nome da função `print` é evidente por si mesmo).

Claro que, se vai fazer uso de qualquer função já existente, não tem influência no seu nome, mas quando começar a escrever as suas próprias funções, deve considerar cuidadosamente a sua escolha de nomes.

Como dissemos antes, uma função pode ter:

- um **efeito**;
- um **resultado**.

Há também uma terceira, muito importante, componente de função - o(s) **argumento(s)**.

As funções matemáticas normalmente aceitam um argumento, por exemplo, `sen(x)` toma um `x`, que é a medida de um ângulo.

As funções de Python, por outro lado, são mais versáteis. Dependendo das necessidades individuais, elas podem aceitar qualquer número de argumentos - tantos quantos forem necessários para desempenhar as suas tarefas.

> **Nota**: qualquer número inclui zero - algumas funções de Python não precisam de qualquer argumento.

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hello, World!")
```

Apesar do número de argumentos necessários/fornecidos, as funções Python exigem fortemente a presença de **um par de parêntesis** - de abertura e de fecho, respetivamente.

Se quiser entregar um ou mais **argumentos** a uma função, coloque-os **dentro dos parêntesis**. Se for utilizar uma função que não aceita qualquer argumento, ainda assim tem de ter os parêntesis.

> **Nota**: para distinguir palavras comuns de nomes de funções, coloque **um par de parêntesis vazios** após os seus nomes, mesmo que a função correspondente queira um ou mais argumentos. Esta é uma convenção padrão.

A função de que estamos a falar aqui é `print()`. A função `print()` no nosso exemplo tem algum argumento? Claro que sim, mas o que são eles?

O único argumento entregue à função `print()` neste exemplo é uma `string`:

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hello, World!")
```

Como pode ver, **a string é delimitada com aspas** - de facto, as aspas fazem a string - cortam uma parte do código e atribuem-lhe um significado diferente.

Pode imaginar que as aspas dizem algo como: o texto entre nós não é código. Não se destina a ser executado, e deve tomá-lo como está.

Quase tudo o que colocar dentro das aspas será tomado literalmente, não como código, mas como **dados**. Tente jogar com esta string em particular - modificá-la, introduzir algum conteúdo novo, apagar algum do conteúdo existente.

Há mais do que uma maneira de especificar uma string dentro do código Python, mas por agora, esta é suficiente.

> Até agora, aprendeu sobre duas partes importantes do código: a função e a string. Falámos sobre elas em termos de sintaxe, mas agora é altura de os discutir em termos de semântica.

O nome da função (`print` neste caso) juntamente com os *parêntesis* e o(s) *argumento(s)*, formam a **invocação da função**.

Discutiremos isto com mais profundidade em breve, mas devemos dar-lhe umas luzes de momento.

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("Hello, World!")
```

O que acontece quando o Python encontra uma invocação como esta abaixo?

[![.py](https://img.shields.io/badge/-helloWorld.py-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
function_name(argument)
```

Vamos ver:

- Primeiro, o Python verifica se o nome especificado é **legal** (navega nos seus dados internos a fim de encontrar uma função existente com o mesmo nome; se esta pesquisa falhar, o Python aborta o código);
- segundo, o Python verifica se os requisitos da função para o número de argumentos **lhe permitem invocar** a função desta forma (por exemplo, se uma função específica exigir exatamente dois argumentos, qualquer invocação que apresente apenas um argumento será considerada errada, e abortará a execução do código);
- terceiro, o Python **deixa o seu código por um momento** e salta para a função que pretende invocar; claro, também leva o(s) seu(s) argumento(s) e passa-o(s) para a função;
- quarto, a função **executa o seu código**, causa o efeito desejado (se houver um), avalia o(s) resultado(s) desejado(s) (se existir(em)) e termina a sua tarefa;
- finalmente, o Python **regressa ao seu código** (ao local imediatamente após a invocação) e retoma a sua execução.

Três questões importantes têm de ser respondidas assim que possível:

1. Qual é o efeito que a função `print()` causa?

O efeito é muito útil e muito espetacular. A função:

- toma os seus argumentos (pode aceitar mais do que um argumento e pode também aceitar menos do que um argumento);
- converte-os numa forma legível para o ser humano, se necessário (como pode suspeitar, as strings não requerem esta ação, uma vez que a `string` já é legível);
- e envia os dados resultantes para o dispositivo de output (normalmente o console); por outras palavras, qualquer coisa que coloque na função `print()` aparecerá no ecrã.

Não admira, então, que a partir de agora utilize `print()` muito intensivamente para ver os resultados das suas operações e avaliações.

2. Que argumentos `print()` espera?

Quaisquer. Mostrar-lhe-emos em breve que `print()` é capaz de operar com virtualmente todos os tipos de dados oferecidos pelo Python. Strings, números, carateres, valores lógicos, objetos - qualquer um destes pode ser passado com sucesso para `print()`.

3. Que valor é devolvido pela função `print()` ?

Nenhum. O seu efeito é suficiente.

### Instruções Python
Já viu um programa de computador que contém uma invocação de função. Uma **invocação de função** é um dos muitos tipos possíveis de **instruções Python**.

É claro que qualquer programa complexo contém geralmente muito mais instruções do que uma. A questão é: como se acoplam mais do que uma instrução no código Python?

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("The itsy bitsy spider climbed up the waterspout.")
print("Down came the rain and washed the spider out.")
```

[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


The itsy bitsy spider climbed up the waterspout.
Down came the rain and washed the spider out.

A sintaxe de Python é bastante específica nesta área. Ao contrário da maioria das linguagens de programação, o Python requer que não haja mais do que uma instrução numa linha.

Uma linha pode estar vazia (ou seja, pode não conter qualquer instrução) mas não deve conter duas, três ou mais instruções. Isto é estritamente proibido.

> **Nota**: o Python faz uma exceção a esta regra - permite que uma instrução se espalhe por mais do que uma linha (o que pode ser útil quando o seu código contém construções complexas).

### Newline
Mudámos um pouco o exemplo - acrescentámos uma invocação de função vazia `print()` . Chamamos-lhe vazia porque não apresentámos quaisquer argumentos para a função.

Pode vê-lo na janela do editor. Execute o código.

O que acontece?

Se tudo correr bem, deverá ver algo como isto:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("The itsy bitsy spider climbed up the waterspout.")
print()
print("Down came the rain and washed the spider out.")
```

[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


The itsy bitsy spider climbed up the waterspout.

Down came the rain and washed the spider out.

Como pode ver, a invocação vazia `print()` não é tão vazia como se poderia esperar - produz uma linha vazia, ou (esta interpretação também é correta) o seu output é apenas uma **newline**.

Esta não é a única forma de produzir uma newline no console de output. Vamos agora mostrar-lhe outra forma.

### Caractere de escape
Modificámos novamente o código. Olhe com atenção.

Há duas mudanças muito subtis - inserimos um estranho par de carateres dentro da rima. Têm este aspeto: `\n`. Curiosamente, enquanto se pode ver dois carateres, o Python vê um.

A barra invertida `\` tem um significado muito especial quando usado dentro de strings - a isto chama-se o **caratere de escape**.

A palavra *escape* deve ser entendida especificamente - significa que a série de carateres na `string` escapa por um momento (um momento muito curto) para introduzir uma inclusão especial.

Por outras palavras, a barra invertida não significa nada em si, mas é apenas uma espécie de anúncio de que o próximo caratere após a barra invertida também tem um significado diferente.

A letra `n` colocada após a barra invertida vem da palavra **newline** (nova linha).

Tanto a barra invertida como o `n` formam um símbolo especial chamado **um caratere de newline**, que incita o console a iniciar uma nova linha de output.

Execute o código. O seu console deve agora ter este aspeto:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("The itsy bitsy spider\nclimbed up the waterspout.")
print()
print("Down came the rain\nand washed the spider out.")
```

[![.py](https://img.shields.io/badge/-snake.py_output-fff?style=social&logo=Python&logoColor=3776AB)](#)


The itsy bitsy spider
climbed up the waterspout.

Down came the rain
and washed the spider out.

Como pode ver, duas newlines aparecem na canção de embalar, nos locais onde as `\n` foram usadas.

Esta convenção tem duas consequências importantes:

1. Se quiser colocar apenas uma barra invertida dentro de uma string, não se esqueça da sua natureza de escape - tem de a duplicar, por exemplo, uma tal invocação causará um erro:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("\")
```

enquanto esta não o fará:

[![.py](https://img.shields.io/badge/-snake.py_input-fff?style=social&logo=Python&logoColor=3776AB)](#)

```python
print("\\")
```

2. Nem todos os pares de escape (a barra invertida acoplada a outro caratere) significam algo.

Experimente o seu código no editor, execute-o e veja o que acontece.

## Utilizar múltiplos argumentos

## Comentários na linguagem Python
Agora, vamos entender as linhas do que seria o nosso primeiro programa em Python.

A primeira observação que vamos realizar é sobre as palavras iniciadas com `#`. Isso significa que o interpretador que executará o seu programa vai considerar o que vem depois de `#` como comentários. Isso é muito importante para documentar o seu programa, e, às vezes, nos lembrar do que realmente estamos fazendo. Lembrando que esse tipo de comentário é apenas para uma linha.

# 📦 [Python] Package Manager, Bibliotecas e Módulos

Python tem vários gerenciadores de pacotes que facilitam a instalação, atualização e remoção de pacotes e bibliotecas. Os dois gerenciadores de pacotes mais comuns para Python são:

O `pip` - Package Installer for Python é o gerenciador de pacotes padrão para Python. Ele facilita a instalação e gerenciamento de pacotes a partir do Python Package Index (PyPI), que é o repositório oficial de pacotes Python. Você pode usar o pip para instalar pacotes com o seguinte comando:

```sh
# Python 3.12
python3 -m pip install

# Python 3.8
pip install
```

Para quem usa a IDE Spyder (SPYDER, 2020) ou o Google Colab, é necessário colocar uma exclamação antes do comando “pip”, ou seja:

```sh
!pip install
```

Também é possível instalar a partir de um arquivo `requirements.txt`:

```sh
pip install -r requirements.txt
```

Para mais informações, você pode digitar `pip --help` no terminal.

Também é possível atualizar a versão do pip, caso haja atualização de alguma biblioteca para a versão mais recente do Python, veja abaixo:

```sh
# -m pip install --upgrade pip
C:\Users\isaac\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\python.exe -m pip install --upgrade pip
```

O `conda` é um gerenciador de pacotes e um sistema de gerenciamento de ambientes desenvolvido pela Anaconda, uma distribuição popular de Python para ciência de dados e aprendizado de máquina. O conda pode instalar pacotes de diversas fontes, incluindo o PyPI, e também gerenciar ambientes virtuais.

Para instalar um pacote usando conda, você pode usar:

```sh
conda install
```

Caso se seguirmos um código utilizando funções que não são nativas da linguagem de programação, vai então, ao executar o Python, aparece a seguinte mensagem de erro:


NameError: name 'sin' is not defined

> O que isso significa? O Python não reconheceu a função seno. Isso acontece porque muitas funcionalidades do Python estão disponíveis em bibliotecas adicionais, chamadas de **módulos**.

Embora o Python tenha muitas funções internas, como o `print()`, também possui um conjunto de bibliotecas-padrão, que são programas em Python que podem ser incluídos no seu programa. Usaremos o módulo math, pois ele disponibiliza diversas funções matemáticas. Existem certas formas de “chamar” esses módulos no seu programa, a seguir veremos algumas.

### [Python] BIBLIOTECA PADRÃO
A **biblioteca padrão Python** consiste em milhares de **funções**, **métodos** e **classes** relacionados a determinada finalidade e organizados em componentes chamados **módulos**. São **mais de 200 módulos** que dão suporte, entre outras coisas, a:

- Operações matemáticas;
- Interface gráfica com o usuário (GUI);
- Funções matemáticas e geração de números pseudoaleatórios.


Atenção! É importante lembrar dos conceitos de classes e objetos, pois eles são os principais conceitos do paradigma de programação orientada a objeto. As classes são fábricas, que podem gerar instâncias chamadas objetos. Uma classe Pessoa, por exemplo, pode ter como atributos nome e CPF. Ao gerar uma instância de Pessoa, com nome João da Silva e CPF 000.000.000-00, temos um objeto.


Saiba+ Para melhor compreensão dos conceitos de classe e objeto, pesquise sobre paradigma orientado a objeto.


## COMO USAR UMA FUNÇÃO DE MÓDULO IMPORTADO
Para usar as **funções** e os **métodos** de **um módulo**, são necessários dois passos:

- Fazer a importação do módulo desejado com a instrução:
```python
import nome_modulo
```

- Chamar a função desejada, precedida do nome do módulo, com a instrução:
```python
nome_modulo.nome_funcao(paramêtros)
```

Como exemplo, vamos importar o módulo **math** (dedicado a operações matemáticas) e calcular a **raiz quadrada** de 5, por meio da função `sqrt()`. Observe a Figura 30:


>>>import math
>>>math.sqrt(5)
2.23606797749979

A partir desse ponto, serão apresentados os principais aspectos dos seguintes módulos:

- `math` usado para operações matemáticas;
- `random` usado para gerar números pseudoaleatórios;
- `smtplib` usado para permitir envio de e-mails;
- `time` usado para implementar contadores temporais;
- `tkinter` usado para desenvolver interfaces gráficas.

## [Python] MÓDULO `MATH`
Esse módulo provê acesso a **funções matemáticas de argumentos reais**. As funções **não podem ser usadas com números complexos**.

O módulo `math` tem as funções listadas na Tabela 7, entre outras:


Função
Retorno


sqrt(x)
Raiz quadrada de x


ceil(x)
Menor inteiro maior ou igual a x


floor(x)
Maior inteiro menor ou igual a x


cos(x)
Cosseno de x


sin(x)
Seno de x


log(x,b)
Logaritmo de x na base b


pi
Valor de Pi (3.141592...)


e
Valor de e (2.718281...)



Saiba+ Para mais informações sobre o módulo math, visite a biblioteca Python.

Vamos ver um exemplo, utilizando o Google Colab, para importar e usar a famosa biblioteca **Math** do Python, que permite utilizar funções matemáticas dentro da linguagem de programação:

A palavra-chave, nessa situação, é `import`, e, logo depois, coloca-se o módulo. Por questão de organização, o ideal é colocar sempre no início do programa.
Quando fazemos isso, temos acesso a todas as funções da biblioteca math, mas devemos informar ao Python de qual biblioteca estamos chamando a função, para isso utilizamos a sintaxe `módulo.função()`. Desse modo, utilizaremos o `math.sin()` e o `math.pi`. Então, nosso programa pode ser escrito da seguinte maneira:

[![Colab](https://img.shields.io/badge/-Math.ipynb-fff?style=social&logo=Google-Colab&logoColor=F9AB00)](https://colab.research.google.com/drive/1LB-mVNz_dy6N7G8iHgYAtzgXplaUuKN1#scrollTo=vByZRBz2gFho)

```python
import math

vo = 300
g = 10
theta = 15
A = (vo**2/g)*math.sin(2*theta*math.pi/180)
print(round(A))
```

Você também pode chamar essa importação limitando a determinada função que você deseja obter, é extremamente recomendado para não pesar a consulta. Em nosso problema inicial, essa forma de “chamar” as funções do módulo sem utilizar o prefixo ficaria:

```python
from math import sin, pi

v0 =300
g=10
theta=15
A= (v0**2/g)*sin(2*theta*pi/180)
print(A)
```

Caso seja necessário utilizar todas as funções da biblioteca, basta usar o comando `from módulo import *`. Veja o exemplo:

```python
>>>from math import *
>>>v0 =300
>>>g=10
>>>theta=15
>>>A= (v0**2/g)*sin(2*theta*pi/180)
>>>print(A)
```

O Python permite também que importemos um módulo e atribuamos a ele um “apelido”, para utilizar como prefixo ao chamar uma função. O comando é `import` módulo as “apelido”. Desse modo, quando utilizar uma função da biblioteca, basta usar o seguinte formato: `apelido.função()`. Por exemplo:

```python
>>>import math as m
>>>v0 =300
>>>g=10
>>>theta=15
>>>A= (v0**2/g)*m.sin(2*theta*m.pi/180)
>>>print(A)
```

Observe que o apelido será `m`; assim, chamamos a função seno com `m.sin()`. De maneira análoga, podemos atribuir “apelidos” para as funções `from módulo import função as apelido`. Por exemplo:

```python
>>>from math import cos as c, sin as s
>>>print(c(0) + s(0))
```

Vamos retornar ao nosso problema do início do conteúdo, ou seja: determinar a posição vertical `y` da bola no tempo `t`. Para construir o gráfico dessa função, utilizaremos o módulo `matplotlib`, usado, em geral, para realizar gráficos em 2D.

O **Matplotlib** é uma biblioteca de software para criação de gráficos e visualizações de dados em geral, feita para e da linguagem de programação Python e sua extensão de matemática **NumPy**.

```python
>>>import numpy as np
>>>import matplotlib.pyplot as plt
>>>v0 = 5 # Velocidade inicial
>>>g = 10 # Aceleração da gravidade
>>>t = np.linspace(0,1,1001) # Tempo
>>>y = v0 * t - 0.5 * g * t ** 2 # Posição vertical
>>>plt.plot(t,y) # plotar o grafico yxt
>>>plt.xlabel('t (s)') # eixo x com t(s)
>>>plt.ylabel('y (m)') # eixo y como y(m)
>>>plt.show() # mostrar a figura
```

Como saída, o Python apresenta o seguinte gráfico:

No programa apresentado, temos que destacar 2 pontos:

1. A função `linspace` retorna um vetor e a construção genérica dele é `linspace(a,b,n)`, onde gera um vetor no intervalo `[a,b]` e com `n` pontos.
2. Quando utilizamos `t` como vetor, a função da altura `y(t)` torna-se também um vetor.

É possível ver qual o caminho do diretório de instalação do python, através da biblioteca `sys`. Veja abaixo:

```python
import sys
sys.executable

# Output: Python installation PATH
```

Mas e se eu quiser utilizar as várias versões da linguagem python ou diferentes bibliotecas instaladas para usar na minha aplicação? Para isso existe o `pipenv` que faz o gerenciamento de pacotes como o `pip` faz e também o ambiente virtual de desenvolvimento, ele basicamente une essas funcionalidades.

```sh
pip install pipenv
```

Para instalar algum pacote nesse ambiente virtual python, basta:

```sh
pipenv install
```

## [Python] MÓDULO `RANDOM`
Esse módulo implementa geradores de números pseudoaleatórios para várias distribuições.

- **Números inteiros**: Para inteiros, existe uma seleção uniforme a partir de um intervalo.

- **Sequências**: Para sequências, existem:
- Uma seleção uniforme de um elemento aleatório;
- Uma função para gerar uma permutação aleatória das posições na lista;
- Uma função para escolher aleatoriamente sem substituição.

## Distribuições de valores reais
A Tabela 8 mostra algumas das principais funções disponíveis para distribuições de valores reais no módulo `random`.

# 🐍 [Python] Aritmética computacional
A Aritmética Computacional, apresentação dos erros comuns na linguagem Python e métodos clássicos de obtenção de raízes de funções não lineares.

Entender a aritmética computacional como ferramenta essencial para os profissionais que utilizaram programação para resolver problemas de modelagem matemática e os possíveis erros que podem ocorrer na execução de algoritmos em uma linguagem específica. Para compreender os conceitos abordados, é necessário prévio conhecimento básico da linguagem Python.

> É necessário aprender os recursos do Python: programação e bibliotecas.

Faremos um programa Python para avaliar uma fórmula simples. Nosso primeiro exemplo diz respeito à programação de um modelo matemático que calcula a altura de um objeto atirado na direção vertical, partindo de uma altura inicial igual a zero. Da 2ª lei de Newton, e ao assumir uma resistência do ar desprezível, obtemos um modelo matemático que determina a posição vertical `y` da bola no tempo `t`:

Formula da equação de movimento vertical sob a aceleração devido à gravidade:


y = vot - 0.5gt2

Onde:

- `V0`: É a velocidade inicial da bola.

- `g`: É a aceleração da gravidade no local, que pode ser aproximado por `10m/s2`.

Para obter a altura ao decorrer do tempo, precisamos saber da velocidade inicial, que, para o nosso exemplo, será de `5m/s`. Então, podemos escrever um arquivo chamado de `primeiroprograma.py`, com o seguinte conteúdo:

```python
# Programa para calcular a altura de um objeto em movimento vertical
v0 = 5 # Velocidade inicial
g = 10 # Aceleração da gravidade
t = 0,5 # Tempo
y = v0 * t - 0,5 * g * t ** 2 # Posição vertical
print(y)
```

`v0 = 5 # Velocidade inicial` Essa linha é uma atribuição de variável, ou seja, toda a vez que aparecer v0, o Python vai entender que é uma representação de 5m/s. Uma maneira simples de entender o que é atribuição seria pensar que o Python gera uma "caixa" no computador (memória) com o nome v0 escrito no topo. O número 5 é, então, colocado nessa caixa. Sempre que o Python, mais tarde, encontra o nome v0 no código, ele encontra a caixa, de modo que o Python tira o número cinco e substitui o nome v0 pelo número. Isso também acontece com as linhas seguintes: g = 10 e t = 0,5.

`y = v0 * t - 0,5 * g * t ** 2 # Posição vertical` O Python já conhece três “caixas” com seus respectivos valores v0, g e t, então, a linha seguinte contém a fórmula do nosso modelo matemático: `y = v0 * t - 0,5 * g * t ** 2`. Novamente, de acordo com suas regras, Python interpreta `*` como multiplicação, `-` como menos e `**` como exponenciação. O Python executa a matemática e atribui o resultado (neste caso, o número `1,25`) à variável de nome `y`.

`print(y)` Na última linha, vem o comando print(y), que é um comando de impressão na tela do valor de y, pois, caso não seja colocado essa função print, o Python executaria a fórmula e atribuiria na “caixa” y o valor de 1,25 e nada aparecia na tela.

Vamos falar um pouco sobre o termo atribuição de variáveis. Observe que utilizamos o operador `=` para atribuir um valor numérico a uma representação, como no exemplo `v0`, `t` e `g`. Nesse ponto, a linguagem de programação difere da linguagem matemática. Para exemplificar essa diferença, usaremos a expressão `x= 4-x`. Veja o que essa expressão significa em cada caso:

- Matemática: É uma simples equação, onde a variável `x = 2`.

- Programação: É necessário saber qual é o valor `x` do lado direito, subtraí-lo de 4 e atribuir esse resultado a x do lado esquerdo. Geralmente, isso acontece em processos iterativos.

Nas próximas seções, serão apresentados alguns comandos e estruturas básicas do Python, lembrando que as seções não são um aprofundamento da linguagem Python, mas o básico para começarmos a trabalhar com a modelagem matemática.

## [Python] Strings
Agora, vamos tratar de recursos adicionais da linguagem para apoiar o desenvolvimento dos códigos: os strings (caracteres). O Python usa a tabela de caracteres, que depende do seu sistema operacional, como, por exemplo, ASCII e UTF-8. Os strings são colocados entre aspas simples (`‘`) ou duplas (`“`) e os caracteres que são não imprimíveis podem ser expressos com a “barra-invertida” (`\`). Seguem alguns exemplos:

- `\n` Cria uma nova linha.

- `\t` Cria uma tabulação.

- `\b` É uma backspace.

Vejamos exemplos de aplicação:


>>>print('abc\nd')
abc
d


>>>>print('abc\td')
abc d

É possível obter o endereço dos caracteres dos strings usando a notação de índices, `string[índice]`, observando que, para o Python, o primeiro caractere tem índice `0` e o último tem índice `-1`. Vejamos alguns exemplos:

```python
>>> palavra = “abcd”
>>> palavra[0]
a
>>>palavra[-1]
d
```

Podemos também fatiar (slices) um string com a notação `string[índiceI:índiceF]`. Com esse comando, o Python nos retorna os caracteres, iniciando com o caractere de `índiceI` (inclusive) e finalizando com o caractere que fica antes do `indiceF`. Isso acontece, pois o `indiceF` é exclusivo. Por exemplo:

```python
>>> palavra[0:2]
ab
```

Observe que o caractere correspondente ao índice `2` não apareceu, pois se `palavra = "abcd"`, então `palavra[0:2]` irá retornar um slice que vai do índice `0` (inclusive) até o índice `2` (exclusive) da string.

No caso de `palavra = "abcd"`, o slice `palavra[0:2]` retornará os caracteres nos índices `0` e `1` da string, ou seja, os caracteres `"ab"`.

Em Python, `str` é um tipo de dados que é usado para representar sequências de caracteres, ou seja, texto. As strings em Python são imutáveis, o que significa que seu conteúdo não pode ser alterado após a criação. O tipo de dados `str` possui várias operações úteis associadas a ele, que tornam Python uma linguagem bastante propícia para manipulação de textos. Por exemplo, você pode concatenar strings, substituir substrings, verificar se uma string começa ou termina com uma determinada substring, contar o número de ocorrências de uma substring e muito mais.

Além disso, você pode acessar caracteres específicos de uma string em Python usando a notação `[]`. Por exemplo, `s[0]` retorna o primeiro caractere da string `s`, e `s[-1]` retorna o último caractere. Você também pode acessar “fatias” ou “slices” de uma string fornecendo os índices do começo e do final da fatia que deseja analisar.

Veja o exemplo:

```python
# Definindo uma variável como um número inteiro
numero = 10

# Usando a função str para converter o número inteiro em uma string
numero_str = str(numero)

# Agora você pode concatenar a string com outras strings
mensagem = "O número é " + numero_str

# Imprime: O número é 10
print(mensagem)
```

Neste exemplo, a função `str()` é usada para converter um número inteiro em uma string. Isso permite que o número seja concatenado com outras strings para criar uma mensagem. Se tentássemos concatenar um número inteiro com uma string sem usar `str()`, Python retornaria um erro, pois não pode adicionar diretamente números e strings.

## [Python] Integer
A representação dos números inteiros positivos na base decimal (`10`) consiste em um número de algarismo (`0,1,2,3,4,5,6,7,8,9`), o qual depende da respectiva posição na representação.

**Exemplo**: O número `179` significa uma (`1`) centena (`100`) mais (`+`) sete (`7`) dezenas (`10`) e nove (`9`) unidades, ou, de forma simplificada, por símbolos matemáticos: `179 = 1x100 + 7x10 + 9`.

Agora, vamos generalizar essa notação matemática. Para isso, vamos adotar como representação para qualquer **digito numérico decimal** a letra `di`, onde `i` é a posição no número `N` com `n+1` dígitos. Por exemplo, no número `179`, o dígito decimal `1` encontra-se na posição `i=2`; `7` na posição `i=1`; e `9` na posição `i=0`. Em uma forma de notação matemática, temos:

image

Onde: `0 ≤ di < 10,i,...n`

Outra notação comum para representar um número inteiro positivo na base `10` é `(dndn-1....di...d1d0 )10`. Essa notação nos leva a outra, caso se deseje representar o número com uma base diferente de `10`, que seria `N=(dndn-1....di...d1d0 )b` , com `b` sendo uma base diferente de `10`; consequentemente, teremos `0 ≤ di < b`.

image

As bases mais comuns na aritmética computacional são:

- `b=n` (Uma base qualquer (1-3...7..9-11...15...17)) - é representado pelos dígitos e vezes a base, onde no final a base é elevada a zero que é igual a 1, com representatividade padrão de: `0 =< di < b`

- `b=2` (Binário) - Os dígitos só podem ser `0` e `1`.

- `b=8` (Octodecimal) - o dígito só vai até dn-1. Por exemplo: di = 0,1,2,3,4,5,6,7

- `b=10` (Decimal) - pegamos o número, esmiussamos ele, multiplicamos pela base que é 10 (dependendo da ordem decimal se for centena ou milhar pode haver um expoente) e somamos com o restante do número. Exemplo: `179 = 1 x 10^2 + 7 x 10 + 9`

- `b=16` (Hexadecimal) - o dígito só vai até dn-1. Por exemplo: di = 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F. Portanto, as letras do alfabeto são representações de números na escala decimal.

Observe que, para `b=2`, por definição, os valores possíveis de `d` são `0` ou `1`; de maneira análoga, quando `b=8`, os valores que podem assumir são `0, 1, 2, 3, 4, 5, 6 e 7`. Para os dígitos maiores que `9`, adotamos as letras do nosso alfabeto, ou seja, `A,B,C`. Nesse caso, para a base hexadecimal (`b=16`), os dígitos possíveis são `0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E e F`, onde `A` corresponde, na base decimal, ao número `10`, `B` corresponde ao `11`, e assim, sucessivamente, até `F`, que corresponde ao `15`. Vejamos alguns exemplos:

image

**Mudança da base b para a base decimal 10 e da base 10 para a base b**: A conversão de um número inteiro positivo, que se encontra em uma base numérica `b` (binária) para a base decimal (`10`), é possível por meio da solução da seguinte expressão:


N=dndn1....di,...d1d0)b=dnxbn+dn1×bn1+...+di×bi+....+d1×b1+d0×b0

Então, veja o exemplo a seguir:


(101)2 = 1x22+0x21+1x20=4+0+1=5
(175)8 = 1x82+7x81+5x80=1x64+7x8+5x1=125
(A2D)16 = Ax162+2x161+Dx160=10x162+2x16+13=(2605)10

De modo geral:


(dn dn-1 di, d1 d0)b=dnbn+dn-1bn-1+...+dibi+...+d1b1+d0b0=(N)10=N

Onde `N` terá outros dígitos na base `10`, como visto nos exemplos anteriores.

Para converter um número inteiro positivo `N` na base `10`, para uma base `b`, é necessário determinar os seus respectivos dígitos: `di`, de `(dn dn-1....di,...d1 d0 )b`. Para entender o procedimento, devemos nos lembrar do **algoritmo da divisão de números inteiros positivos**, onde, ao dividir um número `D` por um número `d`, obtém-se um quociente `q` e um resto `r`; e quando `r=0`, dizemos que a divisão foi exata e o resto só pode assumir os valores de `0,1,2...d-1`. Formalizando por símbolos matemáticos:

```python
D=d×q+r, onde 0≤ r ≤d-1
```

Agora, vamos fazer a divisão de `N`, na base `10`, pela base `b` que deseja se transformar. Dessa forma, `D=N` e `d=b`, sendo necessário descobrir quem será o `q` e o `r`. Lembrando o princípio da igualdade: se `A=b`, então, `B=A`. Podemos observar que, na mudança da base `b` para a base `10`, temos:


dn x bn+dn-1 x bn-1+...+di x bi+...+d1 x b1+d0 x b0=(N)10=N

Logo, pelo princípio da igualdade:


N=dn x bn+dn-1 x bn-1+...+di x bi+...+d1 x b1+d0 x b0

q=dn x bn-1+dn-1 x bn-2+...+di x bi-1+...+d1 x b0

Se colocarmos `b` em evidência no `n-1` primeiros termos da segunda parte da expressão, obtém-se:


N=(dn x bn-1+dn-1 x bn-2+...+di x bi-1+...+d1 x b0) x b+d0

Comparando com `D=dxq+r`, concluímos que:


q=dnxbn-1+dn-1xbn-2+...+djxbi-1+...+d1xb0 e r=d0

Então, quando dividimos o número inteiro positivo `N`, na base `10`, por um número `b` na base desejada, obtemos o resto `r` igual ao último digito d0 da representação de N-decimal na base `b`. Sabemos, pelo exemplo anterior, que `2605 = (A2D)16`, onde a letra `D` é o ultimo dígito na representação da base `16`; de fato, quando dividimos `2605` por `16`, resulta, como quociente, `q=162` e resto `r=13`, que, na representação na base `16`, é a letra `D`.

Portanto, o processo adotado será dividir `N` por `b` e obter `q0` e `r0 = d0`; depois, divide-se `q0` por `b`, resultando em `q1` e r1 = d1, e assim sucessivamente; os respectivos restos serão os dígitos `di`, na base `b`, e o quociente da n-ésima e última divisão será o dn.

Define-se como **bit** (binary digit) o elemento de memória básico de um computador que assume dois estados, que são representados pelos dígitos zero (`0`) e um (`1`). O número de bits disponíveis para uma representação numérica de inteiros positivos corresponde ao maior inteiro que o computador pode representar, ou seja, com `m` bits é possível representar 2m números. Por exemplo, para representar os oito dígitos decimais, seriam necessários três bits, pois 23 = 8, entretanto, para representar `10` dígitos decimais são necessários quatro bits, pois 24 = 16 configurações, o que é excedente, ou seja, no **sistema decimal** há desperdício de bits. Então, a base b é binária se `b=2`. Em um sistema numérico binário, existem apenas dois dígitos possíveis, 0 e 1. Cada dígito é referido como um bit. Portanto, se a base de um sistema numérico é 2, então esse sistema é binário.

A representação dos números inteiros não positivos (inteiros negativos) é realizada utilizando a ideia do bit, ou seja, são convencionadas para os sinais positivo (`+`) ou negativo (`-`) as representações de zero (`0`) ou um (`1`). O único problema dessa representação ocorre em operações para obter os números `+0` e `-0`, que, embora para o computador sejam números diferentes, sabemos que são iguais.

Existem diversas formas de resolver esse problema, como

## [Python] Float
O número real `95,32`, representado na base decimal (`10`), pode ser escrito da seguinte maneira:


9 x 101+5 x 100+3 x 10-1+2 x 10-2

Agora, vamos generalizar essa ideia: um número real `r` na base dez (`10`), com `n+1` dígitos na parte inteira e `m` dígitos na parte fracionária, pode ser representado como:


r=(dn dn-1 ... di, ... d1 d0, d-1 d-2 ... d-m)10
dn x 10n+dn-1 x 10n-1+...+d1 x 101+d0 x 100+d-1 x 10-1
+d-2 x 10-2+...+d-m x 10-m

Utilizando o mesmo raciocínio, um número real `r`, representado em uma base `b`, pode ser escrito da seguinte maneira:


r=(dn dn-1 ... di, ... d1 d0, d-1 d-2 ... d-m)b
dn x bn+dn-1 x bn-1+...+d1 x b1+d0 x b0+d-1 x b-1+d-2 x b-2+...+d-m x b-m


## [Python] Boolean
As expressões booleanas são utilizados para realizar comparações relacionais e retornam verdadeiro (`True`) e falso (`False`). Os operadores são:

- `<` Menor que
- `>` Maior que
- `<=` Menor ou igual a
- `=>` Maior ou igual a
- `==` Igual a
- `!=` Diferente

Os exemplos a seguir mostram a resposta do Python ao comparar dados com as saídas `True` e `False`.

```python
>>> a = 2 # variável a recebe o valor 2 (int)
>>> b = 1.99 # variável b recebe o valor 1.99 (float)
>>> c = ’2’ # variável c recebe o valor 2 em forma de um string
>>> print(a > b) # perceba que o valor de a é maior que o valor de b
True
>>> print(a == c) # note que c recebeu um string ('2') e a um int (2), logo não podem ser iguais
False
>>> print((a > b) and (a != c)) # os dois precisam ser verdadeiros para retornar true
True
>>> print((a > b) or (a == b)) # apenas um precisa ser verdadeiro para retornar true
True
```

## [Python] Mudanças de Bases
Captura de tela 2023-12-01 205602

Vamos aprender a converter as bases, veja os exemplos de bases binárias, octodecimais e hexadecimais: (101)2, (175)8, (A2D)16.

A maioria dos exercícios característicos pela mudança de base é a fase de converter um número na base qualquer pela outra que eu desejo, por exemplo. Se tenho um número da base 10 (decimal) para a base 2 (binário), se esse número for inteiro, basta eu dividir sucessivamente por 2 e eu vou pegar os restos dessa divisão. Agora, se eu quero fazer o processo inverso, com números fracionários, por exemplo, eu não vou dividir, eu vou multiplicar por aquela base. Se eu tiver um número 0.11 e estiver na base 10 e coloca-lo na base 2, eu tenho que multiplicar por 2 e não dividi-lo. A maioria dos exercícios sobre conversão de base, basicamente, são essas duas operações: Dividir e multiplicar pela base que eu desejo.

```python
# 101 - Na base binária pra decimal
1*2**2 + 0*2 + 1*2**0

# Output: 5
```

Já existe uma função intríseca na linguagem de programação Python que faz esse cálculo:

```python
# 175 - Na base octodecimal pra decimal
0b101

# Output: 5
```

```python
# A2D - Na base hexadecimal pra decimal
0o175

# Output: 125
```

```python
# A2D - Na base hexadecimal pra decimal
0xA2D

# Output: 2605
```

```python
# Número binário do número 9
bin(9)

# Output: 0b1001
```

```python
# Número octodecimal do número 125
oct(125)

# Output: 0o175
```

```python
# Número octodecimal do número 125
hex(2605)

# Output: 0xa2d
```

```python
# Notação científica
import numpy as np

print('%1.5f' %np.pi)
print('%1.5e' %np.pi)

'''
Output:
3.14159
3.14159e+00
'''
```

**Exercício**: Qual é a representação do número 125 na base 6?

Para encontrarmos o número desejado, faremos sucessivas divisões por 6 (Base b). Para isso, vamos executar os cálculos utilizando o Python. Usaremos o operador `%` e a função `int`, visto que o número 125 é um inteiro:

```python
N = 125; d0 = (N % 6); # 5
N = int(N/6); d1 = (N % 6); # 2
N = int(N/6); d2 = (N % 6); # 3

print(str(d2)+str(d1)+str(d0))

# Output: 325 = (325)6
```

### [Python] Mudança de base dez (10) para a base b
Para realizarmos a mudança de base dez para uma representação na base b de um número real no formato `r = i,f`; onde:

- `i`: É a parte inteira.
- `f`: É a parte fracionária.

Devemos reparar ambas as partes, ou seja, fazer `r = i + 0,f`; tomando como exemplo `95`, temos `32 = 95 +0,32`. Já vimos nas seções anteriores a transformação da parte inteira; agora, vamos focar somente na parte fracionária `0,f`. Então, vamos considerar que o nosso número real `r` só contém a parte fracionária:

image

O procedimento seria, primeiramente, lembrar que, para transformar um inteiro na base (10) para uma base b, deveríamos dividir o número por b, sucessivamente, e obter os restos. A ideia será a mesma, porém com uma diferença, ao contrário de fazer divisões, faremos multiplicações, ou seja, o primeiro passo será multiplicar o número real `r` pela base que se deseja transformar, `b`. Em símbolos matemáticos, teríamos:

image

Obtém-se uma parte inteira, `d-1`, e a parte fracionária começa com `d-2`. Multiplicando a parte fracionária restante por b, o resultado será d-2, e, assim, sucessivamente. A seguir, veremos dois exemplos de conversão dos número reais 0,625 e 0,2 para a base binária, ou seja, da base dois (2):

image

Esses dois exemplos ilustram que é possível, numa transformação de base dois de um número real na base 10, obter um resultado cuja representação possui um número finito de zeros e um ou um número infinito (dízima periódica) de zeros e um.

### [Python] Mudança da base b para a base 10
Seja r um número real na base b, `(0, d-1d-2....d-m )b`, para obter a sua representação na base 10, basta aplicar a fórmula matemática já vista na seção anterior:

image

Por exemplo, converter o número (0,561)8 para a base 10, aplicando a fórmula:

image

**Mudança da base b para a base 2**: Para encontrar o número desejado, primeiramente, vamos converter o número binário da base 2 para a base 10, e depois precisamos converter o número para a base b, lembrando que primeiro vem a parte inteira, se o número em questão é um número real, depois vem a parte da fracionária, ou seja, é feita a conversão de maneira isolada.

Exemplo: Quero converter na base 4 o número real de base binária

```python
# Conversão da Base Binária (b)2 para a base decimal (b)10
def bin2dec(bin_str):
return int(bin_str, 2)

# Conversão da Base decimal para a base b
def dec2base4(dec):
if dec == 0:
return '0'
else:
result = ''
while dec > 0:
result = str(dec % 4) + result
dec //= 4
return result

# Conversão da base binária para fracionária na base b
def bin_frac2base4_frac(bin_frac_str):
dec_frac = 0
for i, digit in enumerate(bin_frac_str):
dec_frac += int(digit) * (2 ** -(i+1))
base4_frac = ''
while dec_frac > 0:
dec_frac *= 4
digit = int(dec_frac)
base4_frac += str(digit)
dec_frac -= digit
return base4_frac

# Conversão final do binário real para a base b real
def bin_real2base4_real(bin_real_str):
if '.' in bin_real_str:
bin_int_str, bin_frac_str = bin_real_str.split('.')
return dec2base4(bin2dec(bin_int_str)) + '.' + bin_frac2base4_frac(bin_frac_str)
else:
return dec2base4(bin2dec(bin_real_str))

print(bin_real2base4_real('101.01'))

# Output: 11.1 = (11.1)4
```

A conversão de um número real de base binária para a base 4 em Python pode ser um pouco mais complexa, pois envolve a conversão da parte inteira e da parte fracionária separadamente. No exemplo acima, a função `bin_real2base4_real()` primeiro verifica se o número binário é um número real, dividindo-o em uma parte inteira e uma parte fracionária se houver um ponto decimal. Em seguida, ele converte a parte inteira e a parte fracionária separadamente, usando as funções `dec2base4()`, `bin2dec()`, e `bin_frac2base4_frac()`.

Outra forma mais simples de fazer essa mesma operação: Para encontrar o número desejado, primeiramente, vamos converter o número para a base 10, ou seja: 1⋅22+0⋅21+1⋅20+0⋅2-1+1⋅2-2=5,25, agora é converter o número para a base 4. Em Python, basta utilizar o comando:

```python
# Primeiro com a parte inteira 5

N = 5; d0 = (N % 4);
N = int(N/4); d1 = (q % 4);

print(str(d1)+str(d0))
# Output: 11

# Agora, a parte fracionária 0,25

f=0.25
d=1, x=0.000000
d=int(4*f) ; f = 4*f - d; print('d=%d, x =%f' % (d,f))
```

**Notação Científica dos números reais**: Nas ciências, é comum encontramos fenômenos ou grandezas cujos valores são altos ou baixos, por exemplo, na Astronomia ou na Nanotecnologia. Para a representação desses números, é necessária uma quantidade grande de dígitos. A solução desse problema é usar a notação cientifica, cuja representação de um número real é dada da seguinte forma:

image

Onde:

- `M` é um inteiro real não negativo, chamado de **mantissa**.

- `b ≥ 2` é um número inteiro positivo, chamado de **base**.

- `e` é chamado de **expoente**.

No entanto, essa notação possui uma ambiguidade. O número `0,1`, por exemplo, pode ser representado de formas equivalentes:

image

Para resolver essa situação, foi imposta a seguinte condição à mantissa:

image

Essa condição define uma notação **normalizada**.

**Representação em ponto flutuante**: A notação científica abrange todos os números reais e, como sabemos, essa quantidade é infinita, o que torna impossível de ser implementada em um computador que possua uma quantidade finita de dígitos. A solução foi modificar um pouco a normalização da notação científica da seguinte maneira:

image

A notação que usaremos é FP(b,p, emin, emax), para nos referirmos ao número no sistema de ponto flutuante de base `b`, na qual a mantissa possui `p` dígitos (na base b) e os expoentes estão contidos no intervalo fechado [emin, emax], ou seja, `r=0` ou r=±M×be, onde b-1 ≤ M ≤ 1 - b-p.

Tomamos como exemplo um número real representado em ponto flutuante `FP(10,4,-99,99)`. Pode ser escrito de forma genérica como `r=±(0,d-1 d-2 d-3 d-4)×10e`, onde `-99≤ e ≤ 99`. Observe que essa representação não é capaz de representar o número real 0,1x10100, pois o expoente é igual a `100 > 99`, que é o expoente máximo.

> **Atenção**: Em uma modelagem matemática, isso é chamado de **overflow**. De maneira semelhante, 0,1x10-100, e o motivo é o mesmo -100<-99, que é o valor mínimo. Essa situação é definida como **underflow**.

## [Python] Operadores Aritméticos
No Python, podemos utilizar as operações aritméticas usadas na matemática básica. Veja alguns exemplos dos operadores que o Python suporta:

- `+` Adição

- `-` Subtração

- `*` Multiplicação

- `/` Divisão

- `**` Exponenciação

- `%` Resto de divisão

A seguir, apresentaremos alguns exemplos dessas operações e como o Python responde.

```python
>>> s = ’Olá’
>>> t = ’para você’
>>> a = [1, 2, 3]
>>> print(3*s) # Repetição
Olá Olá Olá
>>> print(3*a) # Repetição
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> print(a + [4, 5]) # Adicionando elementos
[1, 2, 3, 4, 5]
>>> print(s + t)
Olá para você
```

Veja outro exemplo:

```python
(9**4 +2)*6 - 1 # 39377
```

O Python usa a mesma hierarquia de cálculo de expressões numéricas da matemática, ou seja, primeiro exponenciação, depois multiplicação ou divisão, e, por último, adição e subtração. Desse modo, calculamos primeiro 9**4 (nove a quarta potência), depois adicionamos 2, multiplicamos por 6 e, por fim, subtraímos por 1 , resultando em 39377.

## [Python] Tuplas e Listas
A **tupla** é uma sequência de objetos arbitrários separados por vírgulas e incluídos entre parênteses. Se a tupla contém um único objeto, uma vírgula final é necessária; por exemplo, `x = (2,)`. Tuplas suportam as mesmas operações que strings e são imutáveis. Veja a seguir um exemplo onde a tupla dados contém outra tupla `(31,10,73)`:

```python
>>> dados = ('Gomes', 'Roberto', (31,10,73)) # Esta é uma tupla
>>> sobrenome, nome, data_de_nascimento = dados # Descompactando a tupla
>>> print(nome)
Roberto
>>> Ano_de_nascimento = data_de_nascimento [3]
>>> print(Ano_de_nascimento)
73
>>> nome_competo = dados [1] + '' + dados [0]
>>> print(nome_completo)
Roberto Gomes
>>> print(rec [0: 2])
('Gomes', 'Roberto')
```

Uma tupla é uma estrutura de dados em muitas linguagens de programação. É semelhante a uma lista, mas geralmente é imutável, o que significa que, uma vez criada, seus elementos não podem ser alterados. Ela pode conter uma sequência de elementos de diferentes tipos e é acessada por meio de índices, assim como as listas, e permite armazenar uma coleção ordenada de elementos, geralmente de tipos diferentes. Os elementos dentro de uma tupla podem ser de qualquer tipo (inteiros, strings, floats, etc.) e são acessados por meio de índices, assim como em uma lista.

Uma **lista** é semelhante a uma tupla, mas é mutável, de modo que seus elementos e comprimento podem ser mudados. Uma lista é identificada colocando-a entre colchetes. Seguem alguns exemplos da operações realizadas em listas:

```python
>>> a = [1.0, 2.0, 3.0] # Crie uma lista
>>> a.append (4.0) # Anexar 4.0 à lista
>>> print(a)
[1,0, 2,0, 3,0, 4,0]
>>> a.insert (0,0.0) # Insira 0.0 na posição 0
print(a)
[0,0, 1,0, 2,0, 3,0, 4,0]
>>> print(len (a) # Determine o comprimento da lista
5
>>> a [2: 4] = [1.0, 1.0, 1.0] # Modifica os elementos selecionados
>>> print(a)
[0.0, 1.0, 1.0, 1.0, 1.0, 4.0]
```

Se `a` for um objeto mutável, como uma lista, a instrução de atribuição `b = a` não resulta em um novo objeto `b`, mas, simplesmente, cria uma nova referência para `a`. Portanto, quaisquer mudanças feitas para `b` serão refletidas em `a`. Veja, a seguir, um exemplo:

```python
>>> a = [1.0, 2.0, 3.0]
>>> b = a
>>> b[0] = 5.0
>>> print(a)
[5.0, 2.0, 3.0]
```

Para criar uma cópia independente de uma lista `a`, use a declaração `c =a[:]`, conforme mostrado no exemplo a seguir:

```python
>>> c = a[:]
>>> c[0] = 1.0
>>> print(a)
[5.0, 2.0, 3.0]
```

Em uma lista, os índices começam com 0. Nesse caso, a lista segue a ordem até 4. Quando utilizamos índices negativos, ele usa a ordem inversa, começando com -1 e indo até -5, ou seja, a[-5] = 1.

```python
a=[1,2,3,4,5]
a[-5]
```

## [Python] Funções (Rotinas)
Até agora, vimos sobre biblioteca de funções, módulos, parâmetros, utilizando, no entanto, o que o Python já tem pronto. É possível também criar a nossa própria **função** e seus respectivos argumentos. Essa necessidade geralmente ocorre quando algum procedimento se repete no programa. Por exemplo, vamos supor que em um problema você tenha que calcular diversos alcances de um lançamento oblíquo para diversos ângulos, respectivamente, 15º, 30º, 45º, 60º e 75º, usando esse programa.

```python
>>>import math
>>>v0 =300
>>>g=10
>>>theta=15
>>>
>>>print(A)
```

Uma solução para isso é criar uma função, e a estrutura para declarar uma função em Python é dada da seguinte maneira, conforme a sintaxe abaixo:

```python
def nome.Da.Minha.Função(Argumentos1, Argumentos2,….Argumentosn):

'''Declarações'''

return valor-de-retorno
```

Outro exemplo: Vemos a estrutura do código quando se trata de acesso de uma função ao chamar as variáveis, arrays e listas da repetição.

[![Colab](https://img.shields.io/badge/-Math.ipynb-fff?style=social&logo=Google-Colab&logoColor=F9AB00)](https://colab.research.google.com/drive/1LB-mVNz_dy6N7G8iHgYAtzgXplaUuKN1#scrollTo=vByZRBz2gFho)

```python
import math # Módulo

def Alcance(v0, theta, g): # Função
return (v0**2/g)*math.sin(2*theta*math.pi/180)

'''Variáveis'''
v0 = 300
theta = [15, 30, 45, 60, 75]
A = []

'''For Loop'''
for i in range(len(theta)):
A.append(Alcance(v0, theta[i], 10))

# Printing
print(A) # 4499.999999999999, 7794.228634059948, 9000.0, 7794.2286340599485, 4499.999999999999
```

A seguir, apresentaremos formas de executar procedimentos condicionados e recursivos, que são importantes e comuns em funções.

## [Python] Estruturas Condicionais
A construção de uma estrutura de condicionais no Python é dada por:

```python
if condições: # se

'''bloco de instruções'''

elif condições: # senão se

'''bloco de instruções'''

else: # senão

'''bloco de instruções'''
```

O exemplo a seguir é uma aplicação da estrutura `if`, onde o script decide se o número é positivo, negativo ou zero:

```python
a = 1.5
def sinal_numerico(a):
if a < 0.0: # Se a menor que zero
sinal = 'negativo'
elif a > 0.0: # Se a maior que zero
sinal = 'positivo'
else: # Senão, ou seja, se a não é maior, nem menor que zero (a igual a zero)
sinal = 'zero'
return sinal
print('a é ' + sinal_numerico(a))
# a é positivo
```

## [Python] Laços de repetição (Loops)
Antes de falarmos sobre o comando `for`, vamos abordar a função `range`. A função `range` retorna uma progressão aritmética de inteiros numa lista com a seguinte estrutura sintática: `range (início,parada,incremento)`, onde:

1. `Início` é um parâmetro opcional e o primeiro valor a ser gerado, quando não indicado, por default é o número zero (0).

2. `Parada` é o limite da progressão, que termina no último valor antes da parada.

3. `Incremento` é um parâmetro opcional e indica o passo da progressão. O default, caso não seja informado, é um (1).

Vejamos uma aplicação:

```python
range(3) # parada indicada 3, como não foram indicados, o início será 0 e incremento será 1
# [0,1,2]
range(2,5,2) # início 2, parada 5 e incremento 2
# [2,4]
range(5,2,-2) # o incremento é negativo
# [5,3]
```

O comando `for` permite que nos informemos sobre os elementos de uma lista. De modo geral, o comando é expresso da seguinte maneira: `for variável in lista : comandos`

Uma grande utilidade da função `range` é construir a lista de iteração, como no exemplo a seguir:

```python
for i in range(1,7): print(i)
# 1 2 3 4 5 6
```

> E sempre o último número da lista de iteração, no caso `7` não é contado.

# 🐍 [Python] Erros na aritmética em pontos flutuantes
vamos analisar o quanto a representação finita dos pontos flutuantes influencia nos números reais. Por exemplo, se verificarmos no Python se `22 = 4`, a resposta será verdadeira, mas quando verificamos se image , a resposta é falsa.

## [Python] Erros de representação
A representação em pontos flutuantes só consegue ser realizada de maneira exata para alguns números. Para outros números reais, poderá indicar algum erro, logicamente supondo que os números não sejam **overflow** ou **underflow**. Definiremos um número real, que esteja contido em um sistema ponto flutuante `FP(b,p, emin, emax )`, de forma exata, de `r=fl(r)`. Caso contrário, obteremos a resposta `r=fl(r) + erro`.

Caso não seja possível representar o número real r no sistema de ponto flutuante com exatidão, existem duas técnicas possíveis `fl(r)`:

- **Arrendondamento por truncamento**: Dada uma mantissa `M` de um número real `r`, com número de dígitos `m>p`, onde `p` é o número de dígitos do sistema de ponto flutuante; define-se o truncamento ao desprezar todos os dígitos a partir da posição `p+1`. Por exemplo, seja o número real, na sua notação cientifica na base `10`, igual a `r=0,341592654 x 10`, se o representarmos num sistema de ponto flutuante `FP(10,4,-99,99)`, então, o resultado será `fl(r) = 0,3415x10`.

- **Arrendondamento por aproximação**: Essa técnica é a mais comum e tem por objetivo reduzir o erro entre o ponto flutuante `fl(r)` e o valor exato `r`, ou seja, o valor mais próximo. Utilizando o exemplo anterior, aproximar `fl(r)` para `0,3416x10` tem um erro menor que aproximar para o valor truncado. O critério arrendondamento por aproximação, às vezes, pode apresentar uma ambiguidade, por exemplo, quando o número real `0,15` for arrendondado para um dígito, os números `0,1` e `0,2` estão igualmente próximos. Para resolver esse problema, foram desenvolvidas várias soluções e, para o sistema binário (b=2) e decimal (b=10), a mais comum é arrendondar de forma que o último dígito seja par.

> **Atenção**: Os arrendondamentos por truncamento e aproximação são realizados somente na mantissa (M), ou seja, não é considerado erro no expoente.

Agora, podemos analisar os erros de representação de um número real e sem perda de generalidade. Vamos considerar somente os números reais positivos exatos na notação cientifica normalizada, ou seja, r = Mxbt, e o seu correspondente no sistema de ponto flutuante fl(r) = mxbt, não necessariamente normalizada. Define-se como erro absoluto `E` por:

image

É possível demonstrar que image para o truncamento e image para aproximação. Define-se como erro relativo `e`:

image

De maneira análoga, pode-se demonstrar que image para o truncamento e image para a aproximação, onde `u` é chamado de unidade de arrendondamento.

> **Exemplo**: Para determinar a unidade de arrendondamento `u` de `FP(2,24,-99,99)` para o truncamento, temos: image

## [Python] Erros nas operações aritméticas
Nesta seção, vamos estudar como são os erros nas operações básicas da matemática: soma, subtração, multiplicação e divisão, quando realizadas em um sistema de ponto flutuante. Primeiramente, vamos descrever de simplificadamente como são realizadas as quatro operações básicas no sistema ponto flutuante. Para a soma e subtração de dois números no mesmo sistema de ponto flutuante, x1=m1ba e x2=m2bc, onde `a>c`, sua soma e subtração podem ser dadas por:

- **Soma**: x1+x2=m1 ba+m2 bc=(m1+m2 ba-c)bc

- **Subtração**: x1-x2=m1 ba-m2 bc=(m1-m2 ba-c)bc

- **Multiplicação**: x1+x2=m1 ba × m2 bc=(m1×m2) ba+c

# 🐍 [Python] Rad - Rapid Applications Development
O **RAD - Rapid Applications Development** trata-se de uma abordagem interativa com o objetivo de produzir o desenvolvimento de software de alta qualidade e trabalho com foco na entrega de aplicações em um período muito inferior ao ciclo de desenvolvimento tradicional de software. Para atingir essa meta, ela trabalha com o ciclo curto baseado em iterações e incrementos que no final de cada ciclo é feito a entrega de um protótipo do usuário que desse modo pode interagir com a aplicação funcional e, assim, fazer críticas e sugestões (feedbacks) que serão úteis para os desenvolvedores aperfeiçoarem a implementação do sistema.

A RAD se diferencia em relação as metodologias tradicionais de desenvolvimento sob vários aspectos, o primeiro sendo sobre o processo de desenvolvimento de software que na RAD segue o modelo iterativo e incremental, na metodologia tradicional de desenvolvimento de software ele seguiria o fluxo linear de desenvolvimento, segunda estrutura seria da estrutura da equipe que na RAD as equipes são pequenas e possuem múltiplas habilidades, no método tradicional tem grandes equipes com funções e habilidades bem definidas, em relação a produtividade, na RAD a produtividade é alta devido aos processos iterativos com os usuários finais, já no desenvolvimento tradicional a produtividade é baixa devido a abordagem linear e rígida, na parte de documentação na RAD é mínima viável, já na prática tradicional a documentação é detalhada e rigorosa com cada estágio de desenvolvimento, em relação ao tempo estimativo de custo, na RAD os projetos são de curta duração e baixo custo de manutenção, na metodologia tradicional o ciclo de vida é longo e com chance de aumento extra com os custos de retrabalho e manutenção. Em relação com a interação do usuário final, na RAD essa interação é ampla e ela ocorre no final de cada iteração, no desenvolvimento tradicional o envolvimento do usuário é somente no início do processo e no final quando ocorre a entrega do sistema. Em relação aos elementos predefinidos na Rad ela trabalha fortemente com essa questão utilizando aplicativos, layouts e modelos que já vem prontos ou pelo menos uma parte pronta, já no desenvolvimento tradicional é necessário desenvolver esses componentes específicos para o projeto com baixa reusabilidade.

Então, o projeto é desenvolvido em etapas e com a inclusão de novas funcionalidades e o resultado da aplicação da RAD é um software com menor custo, menos erros e menor tempo de desenvolvimento. A colaboração entre usuários e desenvolvedores ao longo do projeto é uma característica fundamental da RAD. O projeto é desenvolvido com interações e incrementos de funcionalidades. A operação dessa metodologia pode ocorrer em fases, ou de modo intensivo.

> A RAD pode ser considerada um tipo de técnica ágil. (NAZ & KHAN, 2015).

O desenvolvimento de software tem como objetivo atender as demandas da sociedade, cada vez mais complexas e com abrangência em diversas áreas. Logo nos primórdios da indústria de software, aplicavam-se metodologias que seguiam etapas que não eram revistas e, no final dos projetos, muitas vezes, desenvolvedores e clientes ficavam frustrados com o resultado obtido. Nesse sentido, a necessidade de criar formas mais eficazes de desenvolver sistemas levou à criação da metodologia rápida de desenvolvimento de software, mais conhecida pela sigla em inglês: RAD (Rapid Application Development). Baseia-se na entrega de protótipos para os clientes, a fim de que possam ter uma noção mais clara do progresso do desenvolvimento do software e que também possam colaborar com comentários que permitam aos desenvolvedores fazer alterações para atender as expectativas do cliente.

O desenvolvimento rápido de aplicações RAD (Rapid Application Development) é uma metodologia de desenvolvimento de software com foco na entrega em um período muito inferior ao do ciclo de desenvolvimento tradicional de software. Não se trata de uma entrega final, mas, sim, de um **protótipo do software**. Para que isso seja possível, é feito um planejamento mínimo para obter um protótipo rápido.

> Um protótipo de software é um modelo funcional que equivale funcionalmente a um componente do produto. Ou seja, simula apenas alguns aspectos do produto e é útil para o entendimento e a evolução do sistema final. Na metodologia RAD, existe uma concentração no desenvolvimento dos principais módulos funcionais do sistema. Essa versão inicial, que, apesar de limitada, já é funcional, é chamada de protótipo.

Na metodologia RAD, existe uma concentração no desenvolvimento dos principais módulos funcionais do sistema. Essa versão inicial, que, apesar de limitada, já é funcional, é chamada de **protótipo**:

- É muito útil para a compreensão do sistema
- Serve de demonstração para os clientes
- É mais flexível para mudanças
- Quando está mais evoluído, pode ser integrado ao produto completo para uma entrega mais rápida da versão final

A RAD também pode ser aplicada para aperfeiçoar o treinamento prático de estudantes de computação, auxiliando-os em seus futuros empregos. Isso porque os estudantes podem aplicar o conhecimento adquirido nas aulas para desenvolver sistemas em etapas, conforme é proposto pela RAD. Como será mostrado mais adiante, o fator humano é um importante requisito para a aplicação dessa metodologia, então a sua aplicação para treinar recursos humanos pode acelerar a curva de aprendizado dentro de um curto período.

Os projetos RAD seguem o **modelo iterativo e incremental**. As equipes de desenvolvimento são pequenas, compostas por desenvolvedores, analistas de negócio e representantes de clientes. Um dos aspectos mais importantes deste modelo é garantir que os protótipos desenvolvidos sejam reutilizáveis para o projeto do sistema, ou seja, a ideia não é criar unidades descartáveis. Isso não contradiz o fato de o protótipo ser flexível.

> O desenvolvimento iterativo promove progressos sucessivos, em que o produto é refinado por etapas. No modelo incremental, o software é entregue em pedaços, que são chamados de incrementos. A ideia é que o software seja criado em ciclos curtos, com introdução de funcionalidades, coleta de feedback e revisão.

> O RAD foca no desenvolvimento rápido por meio de iterações frequentes e feedback contínuo.

O modelo RAD foi introduzido pelo consultor de tecnologia e autor James Martin em 1991 (MARTIN, 1991). Surgiu como o reconhecimento da necessidade de atender o competitivo mercado de software, que tem uma demanda contínua por novas aplicações. Uma característica que foi explorada para a formalização da RAD foi a flexibilidade do desenvolvimento de software para projetar modelos de desenvolvimento. Trata-se de uma combinação de sessões JAD, desenvolvimento de protótipos, equipes SWAT, entregas com prazo de entrega e ferramentas CASE.

Portanto, o RAD:

- É muito prática em diversos ambientes modernos de desenvolvimento.
- Apresenta uma abordagem útil para criar aplicações de comércio eletrônico e aplicativos de dispositivos móveis.
- Possui uma velocidade de entrega que pode determinar o posicionamento de uma empresa em um ambiente de mercado muito competitivo.

Portanto, trata-se de uma metodologia importante a ser empregada para que as empresas lancem suas aplicações antes de seus concorrentes.

Observe no fluxo como iniciar um projeto RAD:

1. Uma das formas de iniciar o projeto RAD é através da aplicação da metodologia Joint Application Development (JAD).

> A **Joint Application Development (JAD)** é uma metodologia de desenvolvimento que tem como objetivo melhorar o entendimento do sistema ainda no início do projeto onde a principal característica da metodologia JAD são as oficinas de trabalho, em que desenvolvedores e usuários interagem e colaboram para o entendimento dos requisitos do sistema.

2. Trata-se de uma metodologia na qual usuários e analistas projetam o sistema juntos, sob uma liderança em oficinas de trabalho.
3. A ideia é potencializar o resultado do desenvolvimento através de dinâmicas de grupo.
4. Ou seja, definir os objetivos e as aplicações do sistema, desde a geração de telas até a geração de relatórios.
Tem como princípios: dinâmica de grupo; recursos audiovisuais; processo organizado e racional; a escolha do local; documentação com a abordagem WYSIWIG – “O que você vê é o que você obtém”.

A RAD foi a precursora do gerenciamento ágil de projetos. As características de prototipagem rápida e ciclos de liberação e iterações mais curtos fortaleceram o posicionamento da RAD como um método eficaz no desenvolvimento de software, tornando-se cada vez mais popular entre as empresas ágeis que procuram métodos que acompanhem o crescimento de suas necessidades comerciais e de clientes. Trata-se de uma metodologia orientada pelo feedback do usuário, e não por um planejamento detalhado e caro.

Os métodos tradicionais de desenvolvimento de software, como, por exemplo, a metodologia de desenvolvimento **cascata** (waterfall), seguem modelos rígidos de processo. Isso significa que, nesses modelos tradicionais, os clientes são pressionados a estabelecer os requisitos antes do início do projeto. A iteração ao longo do projeto é baixa, o que complica o processo de mudança para novos requisitos e ajustes de viabilidade.

A metodologia RAD combina diversas técnicas para acelerar o desenvolvimento de aplicações de software. Outra forma pela qual a RAD é conhecida é como “Construção Rápida de Aplicações”, do inglês Rapid Application Building (RAB). Um dos principais elementos da RAD é o desenvolvimento de protótipos para chegar ao sistema final. Trata-se de um modelo adaptativo, uma vez que o desenvolvimento é feito em iterações em que mudanças podem ser realizadas a partir dos comentários do usuário. A ênfase está na criação rápida de um protótipo, em vez de um planejamento detalhado.

A metodologia RAD possui quatro elementos fundamentais:

- USO DE FERRAMENTAS PARA DAR SUPORTE AO DESENVOLVIMENTO: O uso de ferramentas CASE facilita a automação no desenvolvimento de sistemas. Isso é obtido através de recursos como geração de código e verificação automática de erros de consistência. As ferramentas CASE, portanto, são usadas para gerar protótipos, dando, assim, suporte ao desenvolvimento iterativo, possibilitando que os usuários finais acompanhem a evolução do sistema à medida que ele está sendo construído.

- METODOLOGIA BEM DEFINIDA: É seguido um processo formal de desenvolvimento com atividades em etapas e entregas intermediárias. As tarefas são organizadas de modo a não negligenciar nenhum dos aspectos pré-acordados, e as técnicas são documentadas para garantir que uma tarefa seja executada da maneira correta.

- PESSOAS: Deve haver treinamento das pessoas tanto na metodologia de trabalho como no uso das ferramentas. As tarefas devem ser distribuídas por pequenas equipes, que devem trabalhar bem juntas.

- GESTÃO: O gerenciamento do projeto deve ser feito com rapidez. Isso é obtido através de oficinas de Planejamento de Requisitos e Design de Sistema para extrair rapidamente os requisitos dos usuários. Além disso, deve ser feita alocação de tempo fixo (**Timebox**) para entregar iterativamente o sistema para os usuários.

> Timebox é o tempo máximo estabelecido para atingir as metas, tomar uma decisão ou executar um conjunto de tarefas.

Além disso, existem dois tipos de projetos RAD:

- **Intensivo**: No tipo de projeto intensivo, uma equipe de desenvolvedores e usuários trabalham por um curto período (algumas semanas) e, ao final desse tempo, espera-se que produza um produto que seja utilizável.

- **Faseado**: Um projeto em fases é aquele distribuído por um longo período. Esses projetos são normalmente iniciados por um workshop JAD. As fases subsequentes do projeto são geralmente organizadas em termos de entrega e demonstração de protótipos incrementais. O objetivo é refinar continuamente o protótipo, tornando-o algo que seja entregue no final do timebox.

Como visto até aqui, está claro que a criação rápida de protótipo é a base da RAD. Nas situações em que os projetos são orientados por requisitos de interface do usuário, o desenvolvimento de protótipo é uma escolha muito adequada, pois é normal que o usuário crie a ideia de como a interface do sistema deve ficar ao longo do desenvolvimento do projeto. O desenvolvimento rápido de protótipos tem como pré-requisito o uso de ferramentas com suporte a componentes gráficos. No mercado, desde a década de 1990, existiam diversas ferramentas para esse fim, em que os programadores simplesmente podem selecionar um componente gráfico e arrastá-lo para um formulário. Desse modo, as interações com os usuários finais são mais produtivas, pois, constantemente, recebem um software operacional.

Constantemente, os programadores são pressionados a entregar as aplicações em prazos curtos e, muitas vezes, sabe-se com antecedência que o projeto terá de passar por modificações ao longo do desenvolvimento. Essas situações são exemplos em que o desenvolvimento rápido é bastante útil, pois ele está embasado exatamente na entrega rápida de protótipos que incorporam os comentários e as solicitações dos usuários a cada entrega. Para ser eficaz, no entanto, a RAD tem alguns requisitos que não são triviais. Alguns requisitos relacionados aos recursos humanos são os seguintes:

- Equipe de desenvolvedores qualificada e motivada.
- Usuários comprometidos com a participação ativa ao longo do projeto.
- Comprometimento para atingir o resultado satisfatório.

O desenvolvimento baseado na entrega de protótipos funcionais busca dar a oportunidade para que o usuário possa interagir com o projeto antes de receber o sistema final. Dessa forma, poderá fazer comentários e solicitações que guiarão os desenvolvedores na confecção do produto que atenda às suas expectativas sob o ponto de vista de funcionalidades, recursos, interatividade do sistema (experiência do usuário), relatórios, gráficos, entre outros.

O RAD é baseado em alguns princípios básicos, que são (FITZGERALD, 1998):

- ENVOLVIMENTO ATIVO DOS USUÁRIOS: A metodologia RAD reconhece que o envolvimento do usuário é necessário para reduzir problemas caros de obtenção de requisitos. Além disso, os usuários podem rejeitar completamente os sistemas, se não estiverem suficientemente envolvidos no desenvolvimento. No centro da abordagem da RAD, estão as oficinas de design de aplicativos conjuntos (JAD) e planejamento de requisitos conjuntos.

- EQUIPES PEQUENAS COM PODER DE DECISÃO: As vantagens da elaboração de equipes pequenas estão na redução de ruídos de comunicação e na minimização de atrasos devido à burocracia que a hierarquia de uma metodologia tradicional impõe. Em relação aos ruídos de comunicação, os canais que tratam dessa área aumentam proporcionalmente ao tamanho da equipe, portanto equipes pequenas evitam a distorção e o conflito na comunicação. A respeito da redução do tempo, empoderar a equipe aumenta as chances de cumprir os prazos por causa da responsabilidade de tomada de decisão. As equipes têm o poder de tomar decisões sobre o design (embora as mudanças sejam reversíveis).

- ENTREGA FREQUENTE DE PRODUTOS: Diferentemente das metodologias de desenvolvimento tradicionais, em que os projetos podem levar muito tempo para serem concluídos, a RAD procura reduzir o tempo de desenvolvimento. Portanto, prazos mais curtos para o desenvolvimento são uma característica importante. Em vez de se concentrar no processo, a RAD tem como premissa a entrega de produtos que satisfazem os requisitos funcionais.

- DESENVOLVIMENTO INCREMENTAL E ITERATIVO: Outro princípio fundamental do RAD é que os sistemas evoluem de forma incremental em cada iteração. A cada nova iteração, surgem novos requisitos que são incorporados ao sistema. Desse modo, os sistemas evoluem através da prototipagem iterativa. Existe um entendimento no RAD que a especificação de requisitos é um processo não determinístico e que evolui à medida que desenvolvedores e usuários interagem com o protótipo do sistema.

- ABORDAGEM TOP-DOWN: Uma vez que, na metodologia RAD, os requisitos não precisam ser completamente definidos logo no início do projeto, eles são especificados em um nível apropriado ao conhecimento disponível no momento. Estes são então elaborados através de prototipagem incremental. Os sistemas são elaborados e confeccionados à medida que o conhecimento cresce. Além disso, como se trata de uma abordagem de “cima para baixo” caracterizada por um curto período, todas as decisões são consideradas reversíveis rapidamente.

- UTILIZAÇÃO DE FERRAMENTAS DE AUTOMAÇÃO (CASE): Trata-se de usar programas que facilitem a automação de processos, criação de diagramas, realização de testes e quaisquer tarefas que facilitem as entregas dentro dos prazos pré-estabelecidos e, obviamente, com qualidade. Além disso, essas ferramentas facilitam a reutilização de componentes que podem ser usados ao longo do projeto.

O ponto fundamental na metodologia RAD é que se trata de uma abordagem colaborativa entre todas as partes interessadas, que são: patrocinadores, desenvolvedores e usuários ao longo da vida de um projeto.

A RAD precisa ser suportada por ferramentas que auxiliem no desenvolvimento das aplicações rapidamente. Entre as categorias de ferramentas que dão suporte à RAD para desenvolver projetos de software estão:

- Integração de dados
- Ambientes de desenvolvimento
- Ferramentas de coleta de requisitos
- Ferramentas de modelagem de dados
- Ferramentas de geração de código

Desde que a RAD foi formalizada, foram desenvolvidas muitas técnicas para a sua utilização. Cada uma das técnicas tem suas particularidades, mas mantém a essência da RAD. No quadro a seguir, conheça algumas dessas técnicas (Naz; Khan, 2015):

TÉCNICA
PARTICULARIDADE

Modelo CBD
O método que descreve como componentes antigos podem ser reutilizados com os novos.

RepoGuard
É um framework para integração de ferramentas de desenvolvimento com repositórios de código-fonte.

Adição dinâmica ágil
Técnicas usadas para integração do ágil para tornar o projeto mais adaptável.

Método baseado em camadas para desenvolvimento rápido de software
Baseado em camadas que segue o XP.

Análise de projeto de sistema baseado em simulação
Desenvolvimento de ferramentas ágeis baseadas em simulação.

Uso de Ajax no RAD
Prototipagem rápida em aplicativos e ferramentas da Web.

Desenvolvimento de aplicativos multiusuário em ambiente distribuído rapidamente.
Middleware de comunicação.

Programação extrema
Adição de reutilização ao XP.

> **XP**: Extreme Programming (XP) consiste em uma metodologia de desenvolvimento de software que tem como objetivo maximizar a qualidade do software e responder mais rapidamente às mudanças nos requisitos do cliente.

A ideia do uso das técnicas de RAD é de otimizar os resultados obtidos dentro do tempo estimado, que, pela natureza da RAD, é curto. Essencialmente, um software é construído para atender a alguma demanda, ou seja, existe uma razão para que seja confeccionado. Portanto, a interação com os usuários (através das métricas e insights) auxilia o entendimento dos desenvolvedores para construir, agregar e incorporar esse entendimento em um protótipo através de técnicas e ferramentas **que acelerem a entrega e reduzam os desvios de compreensão**. A concordância sobre o propósito do sistema e a sua evolução é muito importante para o sucesso do projeto. Tanto desenvolvedores como clientes devem estar envolvidos em interações formais que fortaleçam o comprometimento de todos.

A pressão por soluções de software confiáveis e em curtos prazos favoreceu a criação da metodologia de desenvolvimento rápido de software (RAD). A ideia de entregar protótipos em um ciclo de desenvolvimento incremental e iterativo permite que o usuário possa ter rapidamente uma visão clara de como o sistema está progredindo e se existe alguma questão relacionada aos requisitos que precisa ser aperfeiçoada. Portanto, a colaboração entre desenvolvedores e usuários suporta o desenvolvimento de especificações mais precisas e validadas.

A metodologia RAD é caracterizada pelo desenvolvimento do projeto através de etapas iterativas e incrementais, onde um protótipo é entregue ao final de cada ciclo. A proposta é que haja redução nas atividades relacionadas ao planejamento em detrimento do processo de desenvolvimento através de um processo que se caracteriza por incrementos de funcionalidades a cada nova iteração. Desse modo, a expectativa é que as equipes produzam mais em menos tempo, maximizando a satisfação do cliente, uma vez que ele é envolvido no processo. Isso ocorre porque a RAD é estruturada para que as partes interessadas interajam e possam detectar a necessidade de alterações do projeto em tempo real, sem a necessidade de completar longos ciclos de desenvolvimento, e os desenvolvedores possam realizar as implementações rapidamente ao longo das iterações.

Vamos explorar em detalhes as quatro fases essenciais da Metodologia RAD (Rapid Application Development). Você aprenderá como essa abordagem ágil transformou o desenvolvimento de software, acelerando a criação de aplicações e promovendo a colaboração eficaz entre equipes de projeto.

# 🐍 [Python] Tratamento de exceções (exceptions)

# 🐍 [Python] Concorrência

# 🐍 [Python] Manipulação de Dados em Arquivos

# 🐍 [Python] Banco de Dados
Para se conectar a um banco de dados utilizando Python ou qualquer outra linguagem é preciso de uma interface para manipular e gerenciar os dados do ambiente do banco de dados, ela pode ser ORM - mapeamento de objeto-relacional (bancos de dados relacionais SQL) ou ODM - mapeamento de objeto-documento (bancos de dados NoSQL).

O **SQLAlchemy** é uma biblioteca de mapeamento objeto-relacional (ORM) em Python, amplamente utilizada para facilitar a interação com bancos de dados relacionais. Ela é uma poderosa ferramenta que, através da programação, não apenas facilita a criação e gerenciamento de banco de dados e tabelas, mas também **abstrai o objeto físico de dados**, isso significa que fazendo o uso do SQLAlchemy, mudando uma simples linha de código, chamada de string de conexão, nós podemos mudar completamente o banco de dados utilizado sem que seja preciso alterar nada mais na nossa aplicação. Ela permite aos desenvolvedores escrever código Python para manipular dados em um banco de dados relacional, abstraindo as complexidades do SQL puro e oferecendo uma interface mais orientada a objetos.

Principais características do SQLAlchemy:

- Mapeamento objeto-relacional: Permite que os desenvolvedores usem classes Python para representar tabelas no banco de dados, tornando a interação com o banco mais intuitiva e orientada a objetos.

- Expressões SQL e ORM: Oferece a capacidade de escrever consultas SQL usando a linguagem Python, além de fornecer um ORM completo para criar, consultar e modificar dados de maneira mais abstrata.

- Flexibilidade: Suporta uma variedade de bancos de dados relacionais, como PostgreSQL, MySQL, SQLite, Oracle, entre outros, facilitando a portabilidade do código entre diferentes sistemas de banco de dados.

- Controle transacional: Permite o gerenciamento de transações, garantindo a atomicidade das operações de banco de dados.

- Facilidade de uso: Provê uma API consistente e amigável para realizar operações comuns de banco de dados, o que simplifica o processo de interação com os dados e reduz a necessidade de escrever código SQL manualmente.

Em resumo, o SQLAlchemy é uma ferramenta poderosa para interagir com bancos de dados relacionais em Python, oferecendo tanto uma camada de abstração para operações de banco de dados quanto a flexibilidade para escrever consultas SQL personalizadas quando necessário.

# 🐍 [Python] Web Framework

O **FastAPI** é um framework web de código aberto, construído em Python, conhecido por sua alta performance, facilidade de uso e geração automática de documentação interativa. Ele foi desenvolvido para facilitar a criação de APIs (Interface de Programação de Aplicações) de forma rápida e eficiente.

**Flask** é um microframework que é minimalista e fornece apenas os componentes essenciais necessários para criar algum tipo de aplicativo1. Ele é projetado para ser leve e fácil de estender, tornando-o uma boa opção para projetos pequenos ou para desenvolvedores que desejam ter mais controle sobre seu código.

**Django** é um framework de desenvolvimento web de alto nível, escrito em Python, que incentiva o desenvolvimento rápido e limpo de aplicações web. Desenvolvido por uma equipe liderada por Adrian Holovaty e Simon Willison, o Django segue o princípio do "don't repeat yourself" (DRY) e visa facilitar a construção de aplicações web complexas ao fornecer uma estrutura robusta e convenções bem definidas.

Principais características do Django:

- ORM (Object-Relational Mapping): Django inclui um ORM que mapeia objetos Python para tabelas em um banco de dados relacional. Isso facilita a interação com o banco de dados sem a necessidade de escrever SQL diretamente.

- Administração automática: O Django fornece uma interface de administração automática baseada em web que pode ser personalizada para gerenciar os modelos de dados do aplicativo.

- Sistema de URL: O framework possui um sistema de mapeamento de URL que permite associar URLs a funções específicas, facilitando a criação de URLs amigáveis.

- Templates: Django utiliza um sistema de templates que permite separar a lógica de apresentação da lógica de negócios.

- Segurança: O Django inclui recursos de segurança incorporados, como proteção contra CSRF (Cross-Site Request Forgery) e injeção de SQL.

- Sistema de middleware: Permite a criação de componentes reutilizáveis que podem processar as requisições antes que elas atinjam a aplicação principal.

- Suporte a APIs: Django facilita a criação de APIs (Interface de Programação de Aplicações) por meio de pacotes como o Django Rest Framework.

- Escalabilidade: Embora seja fácil começar com Django em projetos menores, o framework é escalável e é usado em muitos aplicativos grandes e de alta carga.

Django é uma escolha popular para desenvolvedores web devido à sua simplicidade, produtividade e à comunidade ativa que o suporta. Ele é amplamente utilizado em diversos tipos de projetos, desde sites simples até aplicações web complexas.

# 🐍 [Python] Interface Gráfica (GUI)
Cada vez mais, nós geramos e consumimos dados para realizarmos atividades que vão desde a abertura de uma conta bancária até as informações que disponibilizamos nas redes sociais. Portanto, é necessário entender como esses dados são gerenciados, pois são importantes para identificar pessoas, preferências, produtos, transações financeiras, entre tantas outras aplicações.

Para fazer esse gerenciamento, as aplicações usam sistemas gerenciadores de banco de dados a fim de realizar operações de inserção, consulta, alteração e exclusão de dados. Além disso, para que o usuário possa interagir de modo eficiente com o sistema, é importante que ele tenha à disposição uma interface gráfica que facilite o seu acesso às funcionalidades implementadas.

A linguagem python aparece como uma opção muito eficaz para atingir esses objetivos, uma vez que oferece recursos para desenvolver aplicações que integrem interface gráfica com operações no banco de dados. No entanto, é possível também integrar Python a outras tecnologias para realizar a funcionalidade de interface gráfica, como frameworks JavaScript (Web e Desktop), mas isso geralmente envolve a execução de um servidor Python separado cujo framework JavaScript chama para executar o código Python.

Ao longo deste conteúdo, apresentaremos alguns dos principais frameworks e as bibliotecas para desenvolver aplicações de interface gráfica, além de explorarmos como realizar aplicações no banco de dados.

A linguagem python possui muitos frameworks para desenvolvimento de aplicações de interface gráfica para interação com o usuário, chamadas, comumente, de GUI (Graphical User Interface). O framework mais comum é o **Tkinter** (python interface to Tcl/Tk-2020) que já faz parte da instalação python, mas existem outros frameworks com características específicas que podem torná-los a escolha adequada para um projeto. Vamos explorar uma variedade de frameworks e bibliotecas populares para criação de interfaces gráficas em Python, incluindo Tkinter, Flexx, CEF Python, Kivy, Pyforms, PyQt, wxPython, PyAutoGUI e PySimpleGUI. Discutiremos as características distintas de cada um, suas aplicações e vantagens, oferecendo uma visão abrangente das opções disponíveis para desenvolvimento de GUIs em Python.

**Tkinter**: É o framework GUI padrão do python. Sua sintaxe é simples, possui muitos componentes para interação com o usuário. Além disso, seu código é aberto e é disponível sob a licença python. Caso ela não esteja instalada na sua versão do python, basta digitar o comando:

```sh
pip install tkinter
```

Nas versões mais recentes da linguagem, o Tkinter já faz parte da dela, então não é preciso instalar. Para testar a instalação, basta escrever o seguinte código na linha de comando:

```python
import tkinter
tkinter._test()
```

Se a instalação ocorreu normalmente, aparecerá uma tela com alguns componentes para que você possa interagir e ter uma impressão inicial do potencial desse framework:

![image](https://github.com/IsaacAlves7/py/assets/61624336/7d2962f4-0f2b-4ecc-8cdf-d3a648493374)

> **Atenção!** Devido à importância do Tkinter, vamos explorá-lo com bastantes detalhes mais à frente.

**Flexx**: É um kit de ferramentas (toolkit) para o desenvolvimento de interfaces gráficas com o usuário implementado em python que faz uso de tecnologia web para sua renderização. O Flexx pode ser usado para criar tanto aplicações de desktop como para web e até mesmo exportar uma aplicação para um documento HTML independente. Para instalar o Flexx, basta digitar o comando: