Python e Orientação a Objetos

221

Transcript of Python e Orientação a Objetos

Page 1: Python e Orientação a Objetos
Page 2: Python e Orientação a Objetos
Page 3: Python e Orientação a Objetos

1

3

14

Sumário

1ComoaprenderPython1.1Oqueérealmenteimportante? 1

1.2Sobreosexercícios 1

1.3Tirandodúvidaseindoalém 2

2OqueéPython2.1Python 3

2.2BreveHistória 3

2.3Interpretador 4

2.4Qualversãoutilizar? 5

2.5Download 6

2.6Cpython,Jython,IronPython? 6

2.7PEP-Oquesãoepraqueservem 7

2.8Ondeusareobjetivos 7

2.9Primeiroprograma 8

2.10ModoInterativo 8

2.11ModoScript 9

2.12Exercício:Modificandooprograma 11

2.13Oquepodedarerrado? 11

3Variáveisetiposembutidos3.1Tiposembutidos(built-ins) 14

3.2Variáveis 15

3.3Parasabermais:Nomesdevariáveis 16

3.4Instruções 17

3.5OperadoresAritméticos 18

3.6Strings 19

3.7Entradadousuário 20

3.8Constantes 22

3.9Comandoif 25

SumárioCaelum

Page 4: Python e Orientação a Objetos

37

44

63

73

3.10Convertendoumastringparainteiro 26

3.11Ocomandoelif 27

3.12Exercícios-Jogodaadivinhação 28

3.13Comandowhile 30

3.14Exercícios-Jogocomwhile 32

3.15Comandofor 34

3.16Exercícios-Utilizandoofornojogo 35

4IntroduçãoaoPycharm4.1IDE 37

4.2Pycharm 38

4.3DownloadeInstalaçãodoPyCharm 39

4.4CriandoumProjeto 40

4.5Executandocódigo 42

4.6PrincipaisAtalhos 43

5Estruturadedados5.1Exercícios:JogodaForca 48

5.2Sequências 51

5.3Conjuntos 56

5.4Dicionários 57

5.5Exercícios:Estruturadedados 59

6Funções6.1Oqueéumafunção? 63

6.2ParâmetrosdeFunção 64

6.3Funçãocomretorno 65

6.4Retornandomúltiplosvalores 66

6.5Exercícios:Funções 67

6.6Númeroarbitráriodeparâmetros(*args) 68

6.7Númeroarbitráriodechaves(**kwargs) 69

6.8Exercício-*argse**kwargs 70

6.9Exercício-Funçãojogar() 71

6.10Móduloseocomandoimport 72

7Arquivos7.1Escritadeumarquivo 73

7.2Fechandoumarquivo 74

7.3Escrevendopalavrasemnovaslinhas 74

7.4Exercícios 75

CaelumSumário

Page 5: Python e Orientação a Objetos

89

110

126

137

7.5Lendoumarquivo 76

7.6Lendolinhaporlinhadoarquivo 77

7.7Gerandoumnúmeroaleatório 78

7.8Exercícios-Leituradearquivos 79

7.9Parasabermais-comandowith 81

7.10Melhorandonossocódigo 81

7.11Exercício-RefatorandoojogodaForca 82

8OrientaçãoaObjetos8.1Funcionalidades 90

8.2Exercício:Criandoumaconta 91

8.3ClasseseObjetos 92

8.4Construtor 94

8.5Métodos 96

8.6Métodoscomretorno 97

8.7Objetossãoacessadosporreferência 98

8.8Métodotransfere 100

8.9Continuandocomatributos 101

8.10Tudoéobjeto 103

8.11Composição 104

8.12Parasabermais:outrosmétodosdeumaclasse 106

8.13Exercício:PrimeiraclassePython 107

9Modificadoresdeacessoemétodosdeclasse9.1Encapsulamento 113

9.2Atributosdeclasse 117

9.3Métodosdeclasse 120

9.4Parasabermais-Slots 121

9.5Exercícios: 123

10PycharmeOrientaçãoaobjetos10.1CriandoumProjeto 126

10.2Criandoumaclasse 128

10.3Executandocódigo 130

10.4Criandométodos 132

10.5Exercício-CriandoprojetobanconoPyCharm 133

11HerançaePolimorfismo11.1Repetindocódigo? 137

11.2Reescritademétodos 140

SumárioCaelum

Page 6: Python e Orientação a Objetos

158

175

191

206

11.3Invocandoométodoreescrito 142

11.4Parasabermais-MétodosMágicos 145

11.5Polimorfismo 145

11.6DuckTyping 149

11.7Exercício:HerançaePolimorfismo 150

11.8ClassesAbstratas 153

11.9Exercícios-classesabstratas 156

12HerançaMúltiplaeInterfaces12.1Problemadodiamante 160

12.2Mix-ins 163

12.3Parasabemais-Tkinter 164

12.4Exercícios-Mix-Ins 165

12.5Interfaces 167

12.6(Opcional)Exercícios-InterfaceseclassesAbstratas 170

13ExceçõeseErros13.1Exceçõesetiposdeerros 180

13.2TratandoExceções 182

13.3Levantandoexceções 183

13.4DefinirumaExceção 184

13.5Parasabermais:finally 185

13.6ÁrvoredeExceções 186

13.7Exercícios:Exceções 187

13.8OutrosErros 190

13.9Parasabermais-depuradordoPython 190

14Collections14.1UserList,UserDicteUserString 191

14.2Parasabermais 193

14.3Collectionsabc 195

14.4ConstruindoumContainer 197

14.5Sized 198

14.6Iterable 198

14.7Exercício:CriandonossaSequência 202

15Apêndice-Python2ouPython3?15.1Quaisasdiferenças? 206

15.2Afunçãoprint() 207

15.3Afunçãoinput() 207

CaelumSumário

Page 7: Python e Orientação a Objetos

209

15.4Divisãodecimal 207

15.5Herança 208

16Apêndice-Instalação16.1InstalandooPythonnoWindows 209

16.2InstalandooPythonnoLinux 213

16.3InstalandooPythonnoMacOS 213

16.4OutrasformasdeutilizaroPython 213

Versão:24.8.11

SumárioCaelum

Page 8: Python e Orientação a Objetos

CAPÍTULO1

Muitoslivros,aopassardoscapítulos,mencionamtodososdetalhesdalinguagem,juntamentecomseusprincípios básicos. Isso acaba criandomuita confusão, em especial porque o estudante não conseguediferenciarexatamenteoqueéessencialaprendernoinício,daquiloquepodeserdeixadoparaestudarmaistarde.

Se uma classe abstrata deve ou não ter ao menos um método abstrato, se o if somente aceitaargumentosbooleanosetodososdetalhessobreclassesinternas,realmentenãodevemserpreocupaçõespara aquele cujoobjetivoprimário é aprenderPython.Esse tipode informação será adquirida comotempoenãoénecessárianoinício.

Nestecurso,separamosessasinformaçõesemquadrosespeciais,jáquesãoinformaçõesextras.Ouentão,apenascitamosemalgumexercícioedeixamosparaoleitorprocurarinformaçõesadicionais,sefordeseuinteresse.

Porfim,faltamencionaralgosobreaprática,quedeveser tratadaseriamente: todososexercíciossãomuito importanteseosdesafiospodemser feitosapóso términodocurso.Dequalquermaneira,recomendamosaosalunosestudarememcasaepraticarembastantecódigoevariações.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

COMOAPRENDERPYTHON

1.1OQUEÉREALMENTEIMPORTANTE?

Agoraéamelhorhoradeaprenderalgonovo

1.2SOBREOSEXERCÍCIOS

1COMOAPRENDERPYTHON 1

Page 9: Python e Orientação a Objetos

Os exercícios do curso variam, de práticos até pesquisas na internet, ou mesmo consultas sobreassuntosavançadosemdeterminadostópicos,paraincitaracuriosidadedoaprendiznatecnologia.

Existe também, emdeterminados capítulos, uma série de desafios.Eles focammais no problemacomputacionalquenalinguagem,porémsãoumaexcelenteformadetreinarasintaxee,principalmente,familiarizar o aluno com as bibliotecas padrões do Python, além de proporcionar um ganho navelocidadededesenvolvimento.

Paratirardúvidasdeexercícios,oudePythonemgeral,recomendamosofórumdoGUJRespostas:

http://www.guj.com.br

Lásuadúvidaserárespondidaprontamente.OGUJfoifundadopordesenvolvedoresdaCaelumehojecontacommaisdeummilhãodemensagens.

Oprincipalrecursooficialparaencontrardocumentação,tutoriaiseatémesmolivrossobrePythonéaPythonSoftwareFoundation(PSF):

https://www.python.org/

DestacamostambémapáginadacomunidadenoBrasil:

https://python.org.br/

Hátambémfórunsoficiaisdacomunidade:

https://python-forum.io/(inglês)

https://python.org.br/lista-de-discussoes/(português)

Foraisso,sinta-seàvontadeparaentraremcontatocomseuinstrutorparatirartodasasdúvidasquesurgiremduranteocurso.

Seoquevocêestábuscandosãolivrosdeapoio,sugerimosconheceraeditoraCasadoCódigo:

https://www.casadocodigo.com.br/

Hátambémcursosonlinequevãoajudá-loairalém,commuitainteraçãocomosinstrutores:

https://www.alura.com.br/

1.3TIRANDODÚVIDASEINDOALÉM

2 1.3TIRANDODÚVIDASEINDOALÉM

Page 10: Python e Orientação a Objetos

CAPÍTULO2

Python é uma linguagem de programação interpretada, orientada a objetos, de alto nível e comsemânticadinâmica.A simplicidadedoPython reduz amanutençãodeumprograma.Python suportamódulosepacotes,queencorajaaprogramaçãomodularizadaereusodecódigos.

É uma das linguagens que mais tem crescido devido sua compatibilidade (roda na maioria dossistemasoperacionais)ecapacidadedeauxiliaroutraslinguagens.ProgramascomoDropbox,Reddit eInstagramsãoescritosemPython.Pythontambéméalinguagemmaispopularparaanálisededadoseconquistouacomunidadecientífica.

Masantesquevocêsepergunteoquecadaumadessascoisasrealmentesignifica,vamoscomeçaradesbravar o mundo Python e entender como funciona essa linguagem de programação que temconquistadocadavezmaisadeptos.

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

Pythonfoicriadaem1990porGuidoVanRossumnoCentrodeMatemáticaStichting(CWI,vejahttp://www.cwi.nl) na Holanda como uma sucessora da linguagemABC. Guido é lembrado como oprincipalautordePython,masoutrosprogramadoresajudaramcommuitascontribuições.

AlinguagemABCfoidesenhadaparausodenãoprogramadores,maslogodeiníciomostroucertas

OQUEÉPYTHON

2.1PYTHON

VocêpodetambémfazerocursodatadessaapostilanaCaelum

2.2BREVEHISTÓRIA

2OQUEÉPYTHON 3

Page 11: Python e Orientação a Objetos

limitaçõeserestrições.Amaiorreclamaçãodosprimeirosalunosnãoprogramadoresdessalinguagemera a presença de regras arbitrárias que as linguagens de programação haviam estabelecidotradicionalmente-muitacoisadebaixonívelaindaerafeitaenãoagradouopúblico.

Guidoentãoselançounatarefadecriarumalinguagemdescriptsimplesquepossuíssealgumasdasmelhores propriedades da ABC. Listas Python, dicionários, declarações básicas e uso obrigatório deindentação-conceitosqueaprenderemosnestecurso-diferenciamPythondalinguagemABC.Guidopretendia que Python fosse uma segunda linguagem para programadores C ou C++ e não umalinguagemprincipalparaprogramadores-oquemaistardesetornouparaosusuáriosdePython.

Em1995,GuidocontinuouseutrabalhoemPythonnaCorporationforNationalResearchInitiatives(CNRI, veja http://www.cnri.reston.va.us/) in Reston, Virginia onde ele lançou outras versões dalinguagem.

Emmaiode2000,GuidoeotimeprincipaldePythonsemudaramparaaBeOpen.comparaformarotimeBeOpenPythonLabs.Emoutubrodomesmoano,otimedaPythonLabssemoveuparaaDigitalCreations(hoje,ZopeCorporation,vejahttp://www.zope.org/).Em2001,aPythonSoftwareFoundation(PSF,vejahttp://www.python.org/psf/),umaorganizaçãosemfinslucrativos,foiformadaespecialmenteparamanteralinguagemehojepossuisuapropriedadeintelectual.AZopeCorporationéummembropatrocinadordaPSF.

TodososlançamentosdePythonsãodecódigoaberto(vejahttp://www.opensource.org).

VocêprovavelmentejáouviuouleuemalgumlugarquePythonéumalinguageminterpretadaouuma linguagem de script. Em certo sentido, também é verdade que Python é tanto uma linguageminterpretadaquantoumalinguagemcompilada.UmcompiladortraduzlinguagemPythonemlinguagemdemáquina-códigoPythonétraduzidoemumcódigointermediárioquedeveserexecutadoporumamáquinavirtualconhecidacomoPVM(PythonVirtualMachine).ÉmuitosimilaraoJava-háaindaumjeito de traduzir programas Python em bytecode Java para JVM (Java Virtual Machine) usando aimplementaçãoJython.

O interpretador faz esta 'tradução' em tempo real para código demáquina, ou seja, em tempo deexecução.Jáocompilador traduzoprogramainteiroemcódigodemáquinadeumasóvezeentãooexecuta,criandoumarquivoquepodeserrodado(executável).Ocompiladorgeraumrelatóriodeerros(casoselesexistam)eointerpretadorinterrompeatraduçãoquandoencontraumprimeiroerro.

Em geral, o tempo de execução de um código compilado émenor que um interpretado já que ocompilado é inteiramente traduzido antes de sua execução. Enquanto o interpretado é traduzidoinstrução por instrução. Python é uma linguagem interpretada mas, assim como Java, passa por umprocessodecompilação.UmcódigofonteJavaéprimeiramentecompiladoparaumbytecodeedepois

2.3INTERPRETADOR

4 2.3INTERPRETADOR

Page 12: Python e Orientação a Objetos

interpretadoporumamáquinavirtual.

Masdevemoscompilar scriptPython?Comocompilar?Normalmente, nãoprecisamos fazernadadisso porque o Python está fazendo isso para nós, ou seja, ele faz este passo automaticamente. Naverdade,éointerpretadorPyhton,oCPython.AdiferençaéqueemJavaémaisclaraessaseparação,oprogramadorcompilaedepoisexecutaocódigo.

CPythonéumaimplementaçãodalinguagemPython.Parafacilitaroentendimento,imaginequeéum pacote que vem com um compilador e um interpretador Python (no caso, umaMáquina VirtualPython) além de outras ferramentas para usar e manter o Python. CPython é a implementação dereferência(aquevocêinstaladositehttp://python.org).

Para quem está começando, a primeira dúvida na hora da instalação é qual versão do Pythondevemosbaixar.Aqui,dependedoquesedesejafazer.OPython3aindapossuialgumasdesvantagensemrelaçãoaversão2,comoosuportedebibliotecas(queémaisreduzido)epelofatodamaioriadasdistribuiçõesLinuxeoMacOSaindautilizaremaversão2comopadrãoemseus sistemas.Porém,oPython3émaismaduroemaisrecomendávelparaouso.

ExistemcasosqueexigemoPython2aoinvésdoPython3comoimplementaralgoemumambienteque o programador não controla ou quando precisa utilizar algum pacote/módulo específico que nãopossui versão compatível com Python3.Vale ressaltar para quem deseja utilizar uma implementaçãoalternativa do Python, como o IronPython ou Jython, que o suporte ao Python3 ainda é bastantelimitado.

Atualmenteexisteaferramenta2to3quepermitequecódigoPython3sejageradoapartirdecódigoPython2. Há também a ferramenta 3to2, que visa converter o código Python3 de volta ao códigoPython2. No entanto, é improvável que o código que faz uso intenso de recursos do Python3 sejaconvertidocomsucesso.

2.4QUALVERSÃOUTILIZAR?

2.4QUALVERSÃOUTILIZAR? 5

Page 13: Python e Orientação a Objetos

PARASABERMAIS:MÓDULOFUTURE

O módulo future do Python2 contém bibliotecas que fazem uma ponte entre as versõesanterioreseasmaisrecentes.Bastaimportarebibliotecafuture:

>>>import__future__

Paraqueváriasferramentasdisponíveisnaversão3funcionemnaversão2,ouseja,omódulo__future__ permite usar funcionalidades do Python3 no Python2. Mas cuidado, algumas

funcionalidades são sobrescritas e é importante sempre checar a documentação:https://docs.python.org/3/library/\_\_future\_\_.html

Optamos pelo uso da versão mais recente para este curso, a versão 3.6, e vamos introduzir asdiferençasdaversãoPython2emcomentáriosduranteoscapítulosenosapêndicesdaapostila.

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Comodito acima, o Python já vem instalado nos sistemasLinux eMacOS,mas será necessáriofazer o download da última versão (Python 3.6) para acompanhar a apostila. O Python não veminstaladoporpadrãonoWindowseodownloaddeveráserfeitonositehttps://www.python.org/,alémdealgumasconfiguraçõesextras(vejaapêndicedestaapostilasobreinstalação).

ExistemoutrasimplementaçõesdalinguagemcomooJythoneoIronPython.Adiferençaéqueestasimplementações são apenas os compiladores.Obytecode gerado pelo Jython é interpretado por uma

Seuslivrosdetecnologiaparecemdoséculopassado?

2.5DOWNLOAD

2.6CPYTHON,JYTHON,IRONPYTHON?

6 2.5DOWNLOAD

Page 14: Python e Orientação a Objetos

JVM(JavaVirtualMachine)eobytecodedoIronPythonporumaVirtualMachine.NET.

Outra implementação que vem crescendo é o PyPy, uma implementação escrita em Python quepossuiumaVirtualMachinePython.ÉmaisvelozdoqueoCPythonevemcomatecnologiaJIT(JustInTime)quejá"traduz"ocódigofonteemcódigodemáquina.

OCompiladorPythontraduzumprograma.pyparabytecode -elecriaumarquivocorrespondentechamadoprograma.cpy.Sequisermosverobytecodepeloterminal,bastausaromódulodisassembler(dis)quesuportaanálisedobytecodedoCPython,desmontando-o.Vocêpodechecaradocumentaçãoaqui:https://docs.python.org/3/library/dis.html.

PEP,PythonEnhancementProposalsouPropostasparaMelhoramentonoPython,comoonomedizsão propostas de aprimoramento ou de novas funcionalidades para a linguagem. Qualquer um podeescrever uma proposta e a comunidade Python testa, avalia e decide se deve ou não fazer parte dalinguagem.Casoaprovado,orecursoéliberadoparaaspróximasversões.

NositeoficialdoPython(https://www.python.org/)vocêpodechecartodasasPEPsdalinguagem.APEP0éaquelaquecontémoíndicedetodasaspropostasdeaprimoramentodoPythonepodeseracessadaaqui:https://www.python.org/dev/peps.

Ao longo do curso, de acordo com a aprendizagem e uso de certas funcionalidades, citaremosalgumasPEPsmaisimportantes.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Python é uma linguagem de propósito geral.Muitas vezes precisamos lidar com tarefas laterais:buscardadosemumbancodedados,lerumapáginanainternet,exibirgraficamenteosresultados,criar

2.7PEP-OQUESÃOEPRAQUESERVEM

Agoraéamelhorhoradeaprenderalgonovo

2.8ONDEUSAREOBJETIVOS

2.7PEP-OQUESÃOEPRAQUESERVEM 7

Page 15: Python e Orientação a Objetos

planilhasetc.EPythonpossuiváriosmódulosprontospararealizaressastarefas.

PoresseeoutrosmotivosquePythonganhougrandepopularidadenacomunidadecientífica.Alémdisso,Pythonéextremamente legíveleuma linguagemexpressiva,ouseja,de fácilcompreensão.Asciências,poroutrolado,possuemraciocínioessencialmentecomplicadoeseriaumproblemaadicionalparacientistasconhecerem,alémdeseuassuntodepesquisa,assuntoscomplexosdeumprogramadecomputador como alocação de memória, gerenciamento de recursos etc. O Python faz issoautomaticamentedemaneiraeficiente,possibilitandoocientistaseconcentrarnoproblemaestudado.

Vamosparanossoprimeirocódigo!Umprogramaqueimprimeumamensagemsimples.

Paramostrarumamensagemespecífica,fazemos:

print('MinhaprimeiraaplicaçãoPython!')

Certo,masondedigitaressecomando?ComorodarumainstruçãoPython?

Iremos,primeiro,aprenderomodointerativoutilizandooterminal(LinuxeMacOS)ouopromptdecomando(Windows)pararodaroprogramaacima.Abraoterminaledigite:

dev@caelum:~$python3

IssovaiabriromodointerativodoPythonnaversão3.6dalinguagem,tambémchamadodeconsoledoPython.Apósdigitarestecomando,asseguinteslinhasirãoaparecernoseuconsole:

Python3.6.4(default,Jan282018,00:00:00)[GCC4.8.4]onlinuxType"help","copyright","credits"or"license"formoreinformation.>>>

AprimeiralinhaindicaqueaversãoutilizadadoPythonéaversão3.6.4.Asegundaindicaosistemaoperacional(nocaso,oLinux).Aterceiramostraalgumaspalavraschavesdointerpretadorparaacessaralgumasinformações-digitealgumadelaseaperteENTERparatestar.

O'>>>'indicaqueentramosnomodointerativodoPythonebastacomeçaraescreveroscomandos.VamosentãoescrevernossoprimeiroprogramaPython:

>>>print('MinhaprimeiraaplicaçãoPython!')

AoapertarENTER,temos:

>>>print('MinhaprimeiraaplicaçãoPython!')MinhaprimeiraaplicaçãoPython!

Oprint()éumafunçãodoPythonutilizadaparaimprimiralgumamensagemnatela.Maisdetalhes

2.9PRIMEIROPROGRAMA

2.10MODOINTERATIVO

8 2.9PRIMEIROPROGRAMA

Page 16: Python e Orientação a Objetos

sobre funções são tratados em um capítulo específico desta apostila. Neste momento, entenda umafunçãocomoumafuncionalidadeprontaquealinguagemfornece.

Umamensagemdeveestardelimitadaentreaspassimples('')ouduplas(""),comofeitonoexemploacimacomamensagem:'MinhaprimeiraaplicaçãoPython!'.Ointerpretador,nomodointerativo,jávaimostrarasaídadestecomandonoconsole,logoabaixodele.

Maseseumprogramapossuir1.000linhasdecódigo?Teremosquedigitaressasmillinhastodasasvezes para rodar o programa? Isso, obviamente, seria um problema. Existe outro modo dedesenvolvimentonoPythonmaisutilizado,queevitadigitarumprograma longonoconsole todavezqueprecisarexecutá-lo.

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

Omodo interativo émais utilizado para testes, enquanto que omodo script é mais comumenteutilizado na hora de desenvolver. No modo script, isolamos o código Python em um arquivo comextensão.py.Dessamaneira,ocódigoéescritoumaúnicavezeexecutadopelointerpretadoratravésdocomandopython3(ouocomandopythonseestiverutilizandooPython2).

Abraumeditordetextodesuapreferênciaeescrevaoprogramaanteriornele:

print('MinhaprimeiraaplicaçãoPython!')

Salveoarquivocomoprograma.py.Paraexecutá-lo,abraoterminal,navegueatéodiretórioondeseencontraoarquivoprograma.pyedigite:

dev@caelum:~$python3programa.py

AoapertarENTER,vaiaparecernoconsole:

dev@caelum:~$python3programa.py

EditoraCasadoCódigocomlivrosdeumaformadiferente

2.11MODOSCRIPT

2.11MODOSCRIPT 9

Page 17: Python e Orientação a Objetos

MinhaprimeiraaplicaçãoPython!

Vejaqueagora isolamosocódigoemumaarquivoeoexecutamosatravésdocomandopython3.MasnãodevemoscompilarscriptPython?Comocompilar?

NormalmentenãoprecisamosfazernadadissoporqueoPythonestáfazendoissonosbastidores,ouseja, ele faz este passo automaticamente. Se por algum motivo você queira compilar um programaPythonmanualmente,vocêdeveusaromódulopy_compilenoconsoledoPython:

>>>importpy_compile>>>py_compile.compile('programa.py')'__pycache__/programa.cpython-34.pyc'

O comando import importa o módulo py_compile que disponibiliza a função compile(). Oupodemosobteromesmoresultadoutilizandooseguintecomandonoterminal:

dev@caelum:~$python3-mpy_compileprograma.py

Também é possível compilar todos os arquivos Python de uma única vez, usando o módulocompileall:

dev@caelum:~$python3-mcompileall

Entretanto,estespassosnãosãonecessários.Oprocessodecompilaçãoé feitoautomaticamenteenãoéprecisorepetirtodoesteprocessopararodarumprogramaPython.

Apenas rodando o programa, sem precisar compilá-lo, note que uma nova pasta chamada"__pycache__"écriada(casoelanãoexista)nomesmodiretórioqueoprogramafoiexecutado.Dentrodestapastaécriadoumarquivoprograma.cpython-34.pyc-estaéaversãocompiladadoprograma,ocódigobytecodegeradopeloCPython.

SemprequeumprogramaPythonéchamado,oPythonchecaráseexisteumaversãocompiladacomaextensão .pyc -estearquivodevesermaisnovodoqueodeextensão .py (seoarquivoexistir).OPythonvaicarregarobytecode,oquevaiaceleraroscript.Casonãoexistaaversãobytecode,oPythoncriaráoarquivobytecodeantesdeiniciaraexecuçãodoprograma.AexecuçãodeumprogramaPythonsignificaaexecuçãodeumcódigobytecodenaPythonVirtualMachine.

10 2.11MODOSCRIPT

Page 18: Python e Orientação a Objetos

TodavezqueumscriptPythonéexecutado,umcódigobytecode écriado.SeumscriptPythonéimportadocomoummódulo,obytecodevaiarmazenarseuarquivo.pyccorrespondente.

Portanto,opassoseguintenãocriaráoarquivobytecode,jáqueointerpretadorvaiverificarquenãoexistenenhumaalteração:

dev@caelum:~$pythonprograma.pyMinhaprimeiraaplicaçãoPython!dev@caelum:~$

1. Altereoprogramaparaimprimirumamensagemdiferente.

2. Altereseuprogramaparaimprimirduaslinhasdecódigoutilizandoafunçãoprint().

3. Sabendo que os caracteres\n representam uma quebra de linha, imprima duas linhas de texto

usandoumaúnicalinhadecódigo.

Nem sempre as coisas acontecem como esperado. O Python tem uma sintaxe própria, umvocabulário próprio. Digitar algo que o interpretador não entende causará um erro no programa.Vejamosalgunsexemplos:

>>>print'MinhaprimeiraaplicaçãoPython!'Traceback(mostrecentcalllast):File"<stdin>",line1print'MinhaprimeiraaplicaçãoPython!'^SyntaxError:Missingparenthesesincallto'print'.Didyoumeanprint('MinhaprimeiraaplicaçãoPython!')?

Nãoseassustecomamensagem.Vamosentenderoqueelaquerdizer.NaprimeiralinhaapareceapalavraTracebackquesignificaalgocomo:"Oqueoprogramaestavafazendoquandoparouporque

algodeerradoaconteceu?".Éporestemotivoqueamensagemmostrecentcalllast(chamada

maisrecente)émostrada.

ATracebackfazreferênciaaumarquivo-queéonomedoarquivoPythonchamadoacimapelo

nomedestdinquepossuimétodosparaleitura,ondeoprogramalêaentradadoteclado.Oprograma

acusaqueesteerroestánaprimeiralinhadoprograma:File"<stdin>",line1.

Logo em seguida é mostrado exatamente a parte do código que gerou o erro: print 'Minha

primeiraaplicaçãoPython!'.Apróximalinhaéamensagemdeerro:SyntaxError.Sevocênão

2.12EXERCÍCIO:MODIFICANDOOPROGRAMA

2.13OQUEPODEDARERRADO?

Esquecerosparênteses

2.12EXERCÍCIO:MODIFICANDOOPROGRAMA 11

Page 19: Python e Orientação a Objetos

faz a menor ideia do que esta mensagem significa é um bom começo e uma boa prática durante aaprendizagempesquisararespeitodelanainternet,assimcomodemaiserrosquepossamsurgir.

Nestecaso,éumSyntaxError,ouseja,ErrodeSintaxe -oPythonnãoentendeuoquefoi

digitado.Amensagemdizquefaltamosparênteses!Então,éfácilacharumerroquandoeleacontece.

AlgumasvezesvocêveráapalavraExceptionemumamensagemdeerro.UmaExceptionéum

problema que ocorre enquanto o código está sendo executado. Já o SyntaxError é um problema

detectadoquandooPythonverificaocódigoantesdeexecutá-lo,ouseja,emtempodecompilação.

O interpretador vai aguardar (continuar imprimindo reticências cada vez que a tecla ENTER for

apertada)atéqueoparêntesesejafechado:

>>>print('MinhaprimeiraaplicaçãoPython!'.........

Nestecasonãoéumaexceçãoouerro,anãoserquevocêdigitequalqueroutracoisaquenãoumfechamentodeparênteseeaperteateclaENTER.

>>>print(MinhaprimeiraaplicaçãoPython!)File"<stdin>",line1print(MinhaprimeiraaplicaçãoPython!)^SyntaxError:invalidsyntax

Maisumavezacusaerrodesintaxe.

Estes foram alguns erros que o programa pode gerar por desatenção do programador. São maiscomunsdeacontecerdoqueseimagina.Outroserrosserãoabordadosemumcapítuloespecíficodestaapostila. Neste momento, iremos aprender outros recursos que a linguagem Python oferece, e nosfamiliarizarcomsuasintaxe.

Esquecerdefecharosparênteses

Esquecerdecolocaramensagementreaspas(simplesouduplas)

12 2.13OQUEPODEDARERRADO?

Page 20: Python e Orientação a Objetos

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

JáconheceoscursosonlineAlura?

2.13OQUEPODEDARERRADO? 13

Page 21: Python e Orientação a Objetos

CAPÍTULO3

NestecapítulovamosconhecerostiposdabibliotecapadrãodoPython.Osprincipaistiposinternossãonúmeros,sequências,mapas,classes,objetoseexceções,masiremosfocarprimeiramentenosnúmerosesequênciasdetexto(strings).Sãoobjetosnativosdalinguagem,recursosquejávêmprontosparausoechamadosdebuilt-ins.

Nesteiníciodaaprendizagem,trabalharemoscomomodointerativo,eaofinalproduziremosumapequenaaplicaçãoemumscript.

Umvalor,comoumnúmerooutexto,éalgocomumemumprograma.Porexemplo,'Hello,World!',1,2,todossãovalores.Estesvaloressãodediferentestipos:1e2sãonúmerosinteirose'HelloWorld!'éumtexto,tambémchamadodeString.Podemosidentificarstringsporquesãodelimitadasporaspas(simplesouduplas)-eéexatamentedessamaneiraqueointerpretadorPythontambémidentificaumastring.

Afunçãoprint()utilizadanocapítuloanteriortambémtrabalhacominteiros:

>>>print(2)2

Veja que aqui não é necessário utilizar aspas por se tratar de umnúmero. Caso você não tenhacertezaqualéotipodeumvalor,podeusarafunçãotype()parachecar:

>>>type('HelloWorld')<class'str'>

>>>type(2)<class'int'>

Stringssãodotipostr(abreviaçãoparastring)einteirosdotipoint(abreviaçãoparainteger).

Ignore a palavra class por enquanto, teremos um capítulo especial para tratar dela. Veremos que

funçõescomotype()eprint()tambémsãotiposembutidosnoPython.

OutrotipoqueexistenoPythonsãoosnúmerosdecimaisquesãodotipofloat(pontoflutuante):

>>>type(3.2)<class'float'>

VARIÁVEISETIPOSEMBUTIDOS

3.1TIPOSEMBUTIDOS(BUILT-INS)

14 3VARIÁVEISETIPOSEMBUTIDOS

Page 22: Python e Orientação a Objetos

Equalseráotipodevalorescomo'2'e'3.2'?Elesseparecemcomnúmerosmassãodelimitadosporaspascomostrings.Utilizeafunçãotype()parafazeraverificação:

>>>type('2')<class'str'>

>>>type('3.2')<class'str'>

Comoestãodelimitadosporaspas,ointerpretadorvaientenderessesvalorescomostrings,ouseja,comotexto.

O Python também possui um tipo específico para números complexos. Números complexos sãodefinidospordoisvalores:aparterealeaparteimaginária.NoPythonéescritonaformareal+imagj.Nocaso,onúmeroimaginário(definidopelaraizde-1echamadode'i'namatemática)édesignadopelaletrajnoPython.Porexemplo:

>>>2+3j>>>type(2+3j)<class'complex'>

2éapartereale3aparteimagináriadonúmerocomplexo.Utilizandoafunçãotype(),podemos

noscertificarqueseutipoécomplex.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Podemospedir para oPython lembrar de umvalor que queiramos utilizar emoutromomento doprograma.OPythonvaiguardarestevaloremumavariável.Variáveléumnomequefazreferênciaaumvalor. É como uma etiqueta que colocamos naquele valor e quando precisarmos usar, chamamospelonomequefoidadonaetiqueta.

Umcomandodeatribuição(osinaldeigualdade=)criaumanovavariáveleatribuiumvaloraela:

Agoraéamelhorhoradeaprenderalgonovo

3.2VARIÁVEIS

3.2VARIÁVEIS 15

Page 23: Python e Orientação a Objetos

>>>mensagem='oi,python''oi,python'

>>>numero=55

>>>pi=3.143.14

Trêsatribuiçõesforamfeitasnestecódigo.Atribuímosavariávelmensagemumastring;avariável

numero um inteiro e a variável pi um valor aproximado do número pi. No modo interativo, o

interpretadormostraoresultadoapóscadaatribuição.

Pararecuperaressesvalores,bastachamarpelosnomesdasvariáveisdefinidasanteriormente:

>>>mensagemoi,python

>>>numero5

>>>pi3.14

Utilizeafunçãotype()paraverificarseustipos:

>>>type(mensagem)<class'str'>

>>>type(numero)<class'int'>

>>>type(pi)<class'float'>

Programadores escolhem nomes para variáveis que sejam semânticos e que ao mesmo tempodocumentem o código. Esses nomes podem ser bem longos, podem conter letras e números. É umaconvenção entre os programadores Python começar a variável com letras minúsculas e utilizar ounderscore(_)parasepararpalavrascomo:meu_nome,numero_de_cadastro,telefone_residencial.Essepadrãoéchamadodesnakecase.Variáveis tambémpodemcomeçar comunderscore (_)mas

deveserevitadoeutilizadoemcasosmaisespecíficos.

Senomearmosnossasvariáveiscomumnomeilegal,ointerpretadorvaiacusarumerrodesintaxe:

>>>1nome='python'File"<stdin>",line11nome='python'^SyntaxError:invalidsyntax

>>>numero@=10File"<stdin>",line1

3.3PARASABERMAIS:NOMESDEVARIÁVEIS

16 3.3PARASABERMAIS:NOMESDEVARIÁVEIS

Page 24: Python e Orientação a Objetos

numero@=10^SyntaxError:invalidsyntax

>>>class='oi'File"<stdin>",line1class=oi^SyntaxError:invalidsyntax

1nomeé ilegalporquecomeçacomumnúmero,numero@ é ilegalporquecontémumcaractere

especial (o@) considerado ilegal para variáveis. Eclass é ilegal porqueclass é umapalavrachave em Python.O interpretador utiliza palavras chaves comopalavras reservadas da linguagem,comoumvocabuláriopróprio.

Pythonpossui33palavrasreservadas:

anddelfromNoneTrueaselifglobalnonlocaltryassertelseifnotwhilebreakexceptimportorwithclassFalseinpassyieldcontinuefinalyisraisedefforlambdareturn

Portanto,nãopodemosutilizaressaspalavrasparanomearnossasvariáveis.

Umainstrução(oucomando)éumaunidadedecódigoqueoPythonpodeexecutar.Porexemplo,afunçãoprint()paraimprimirumamensagemnatelaéumcomando:

>>>print("Hello,World!")Hello,World!

Quandoexecutamosumcomandonomodointerativo,ointerpretadorPythonapresentaoresultado,caso exista, deste comando. Um script contém uma sequência de instruções. Se existir mais de umcomando,osresultadosvãoaparecendoduranteaexecuçãodoprograma:

print(1)x=2print(x)

Eproduzasaída:

12

PararodarumscriptemPython,éprecisoconcentraressescomandosemummesmolugarepedirpara o interpretador executá-los. Criamos um arquivo de extensão .py com estes comandos, comoaprendemosnocapítuloanterior.

Arquivoprograma.py:

3.4INSTRUÇÕES

3.4INSTRUÇÕES 17

Page 25: Python e Orientação a Objetos

Notequedevemosutilizarafunçãoprint()paraexibirosresultadosnatelajáqueomodoscript,

diferentedomodointerativo,nãoexibeosresultadosapósadeclaraçãodevariáveis.

Navegueatéodiretórioondeseencontraoarquivoprograma.pyedigiteocomandonoterminal:

dev@caelum:~$python3programa.py

Quevaigerarasaída:

dev@caelum:~$python3programa.pyoi,python53.14

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

Operadores são símbolos especiais que representamcálculos comoadições emultiplicações.Parafazercálculoscomnúmerosutilizamososoperadores+,-,*,/e**querepresentam,respectivamente,adição,subtração,multiplicação,divisãoepotenciação.

Umaexpressãoéumacombinaçãodevalores,variáveiseoperadores,comox+17,1+1etc.

Quando digitamos uma expressão no modo interativo, o interpretador vai calcular e imprimir oresultado:

>>>1+1

EditoraCasadoCódigocomlivrosdeumaformadiferente

3.5OPERADORESARITMÉTICOS

18 3.5OPERADORESARITMÉTICOS

Page 26: Python e Orientação a Objetos

2

>>>2*36

Tambémpodemosusarvariáveis:

>>>x=1>>>y=3>>>x+y4

>>>x-y-2

>>>x*y3

>>>x/y0.3333333333333333

>>>x**y1

Alémdosoperadorescomentados,temostambémooperador//querepresentaadivisãointeira:

>>>7//23

Eooperadormódulo%queresultanorestodadivisãoentredoisnúmerosinteiros:

>>>7%31

7dividopor3é2egerarestoiguala1.Esseoperadorébemútilquandoqueremoschecarseumnúmeroédivisívelporoutro.

Osprincipaisoperadoressão:

Operação Nome Descrição

a+b adição Somaentreaeb

a-b subtração Diferençaentreaeb

a*b multiplicação Produtoentreaeb

a/b divisão Divisãoentreaeb

a//b divisãointeira Divisãointeiraentreaeb

a%b módulo Restodadivisãoentreaeb

a**b exponenciação aelevadoapotênciadeb

Ooperador+tambémfuncionacomstringsdeumamaneiradiferentedosnúmeros.Elefunciona

3.6STRINGS

3.6STRINGS 19

Page 27: Python e Orientação a Objetos

concatenandostrings,ouseja,juntandoduasstrings:

>>>texto1='oi'>>>texto2='Python'texto1+texto2oiPython

Ooperador* tambémfuncionacomstrings,multiplicando seu conteúdopor um inteiro.Vamos

checaresseresultado:

>>>texto1='python'>>>texto1*3pythonpythonpython

Aomultiplicarmospor3,oPythonreplicaastring trêsvezes.Noteapresençadeumespaçoapósterminardeescreverapalavranastring.Casoomesmonãoestivessepresente,todasaspalavrasgeradasficariam"grudadas",ouseja,nãohaveriaumaseparaçãoautomáticaentreasmesmas.

Stringspossuemmuitas funcionalidadesprontaschamadasdemétodos.Ométodoupper(), por

exemplo,retornaotextoemletrasmaiúsculas.Jáométodocapitalize()retornaotextocapitalizado

(comaprimeiraletraemmaiúscula):

>>>texto1.upper()'PYTHON'

>>>texto1.capitalize()'Python'

Outrasfuncionalidadesdestringsestãopresentesnadocumentaçãoquepodeseracessadanestelink:https://docs.python.org/3/library/stdtypes.html#string-methods

Agora vamos criar mais interatividade e pedir para o usuário entrar com um valor digitado doteclado.

OPythonpossuiuma funçãoquecapturaaentradadevalores:a funçãoinput().Quando essa

funçãoéchamada,oprogramaparaeesperaousuáriodigitaralgumacoisa.QuandoousuárioapertaateclaENTER,oprogramaprocessaeimprimeovalordigitadoemformadestring:

>>>entrada=input()'oipyhton'

>>>print(entrada)'oipython'

Masoidealépediralgoespecíficoaousuárioedizerqualdadoqueremosreceber.Podemospassarumastringparaafunçãoinput():

>>>nome=input("digiteseunome:\n")digiteseunome:

3.7ENTRADADOUSUÁRIO

20 3.7ENTRADADOUSUÁRIO

Page 28: Python e Orientação a Objetos

caelum

>>>print(nome)caelum

O\nnofinalrepresentaumanovalinhaeointerpretadorvaiquebrarumalinhaapósimprimira

string.Porestemotivo,ovalordigitadopelousuárioaparecenapróximalinha.

Com o conteúdo aprendido até aqui já é possível começar a escrever o primeiro script. Crie umarquivoprograma2.pyeacrescenteumcódigoquevaipedirqueousuárioentrecomalgumvalore,emseguida,oprogramadeveimprimirestevalor.

Arquivoprograma2.py:

numero=input('Digiteumnúmero:\n')print(numero)

PodemosmelhorareimprimirumamensagemcomoOnúmerodigitadofoi:

numero=input('Digiteumnúmero:\n')print('Onúmerodigitadofoi'+numero)

Concatenamosastringcomavariávelnumeroutilizandoooperador+.Agora,seousuáriodigitar

o número 2, a saída será O número digitado foi 2. Outra maneira mais elegante é usar a funçãoformat():

print('Onúmerodigitadofoi{}'.format(numero))

Afunçãoformat()vai substituiro{}pelavariávelnumero.Aprincípio, podeparecer uma

alternativapior, jáqueescrevemosmaiscódigoparaconseguiromesmoresultado.Todavia,a funçãoformat() fornece mais facilidades. Suponha que o programa receba dois valores digitados pelo

usuárioeosimprimaemumaúnicamensagem:

nome=input('Digiteseunome')idade=input('Digitesuaidade')print('Seunomeé{}esuaidadeé{}'.format(nome,idade))

Vejaqueessaformafacilitaaimpressãoeformataçãodosdadosumavezquenãoquebraastringemváriaspartescomoaconcatenaçãofaz.Alémdeque,comooperador+,sempretemosquelembrardos

espaçosembrancoentreaspalavras:

print('Seunomeé'+nome+'esuaidadeé'+idade)

Nestecasoafunçãoformat()émaisrecomendadaefacilitanaimpressãodemensagensnatela.

Agoraoscriptestámelhorepodemosexecutá-lopeloterminal:

dev@caelum:~$python3programa2.py

Asaída:

digiteseunome:caelum

3.7ENTRADADOUSUÁRIO 21

Page 29: Python e Orientação a Objetos

digitesuaidade:20Seunomeécaelumesuaidadeé20

PARASABERMAIS:AFUNÇÃOFORMAT()

Afunçãoformat() fazpartedeumconjuntodefunçõesdeformataçãodestringschamada

Formatter. Para mais detalhes, acesse a documentação:https://docs.python.org/3/library/string.html#string.Formatter.

Háoutrasfunçõesdeformataçãoeaformat()éaprincipaldelaseamaisutilizada.Comela,

podemospassarqualquertipodeparâmetro,alémdeserextremamenteútilparaformatarnúmerospassando seu format code. Por exemplo, podemos arredondar o número flutuante 245.2346paraduascasasdecimaisatravésdocódigodeformatação:.2f:

>>>x=245.2346>>>print('{:.2f}'.format(x))245.23

O:.2fdizquequeremosapenasduascasasdecimaisparaavariávelx.Nadocumentação

oficial do Python você acessa os códigos de formatação ou através da PEP 3101:https://www.python.org/dev/peps/pep-3101/.

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

OPythonpossuipoucas constantes embutidas.Asmaisutilizadas sãoTrue,False eNone.Essastambémsãopalavras chavesdoPython,portantopalavras reservadasquenãopodemosutilizar comonomesdevariáveis.

JáconheceoscursosonlineAlura?

3.8CONSTANTES

22 3.8CONSTANTES

Page 30: Python e Orientação a Objetos

TrueeFalse sãovaloresbooleanosquerepresentam,respectivamente,verdadeiroefalso.OPythontambémpossuiafunçãobool(),queretornaTruequandooargumentopassadoéverdadeiro

eretornaFalse,casocontrário.

PodemosrepresentarTrueeFalseatravésdeexpressões.Porexemplo"Onúmero1é iguala

string'1'?".VamosperguntaraoPython:

>>>1=='1'False

print(1=='1')

Ooperador==éusadoparaverificarsealgoéigualaoutro.Nãoconfundircomo=,queatribui

umvaloraumavariável.Tambémpodemosverificarseumnúmeroémaior,utilizandoooperador>,

oumenor(<)doqueoutro:

>>>2>1True

>>>2<1False

Podemostambémutilizarafunçãobool()parafazeraverificação:

>>>bool(3>5)False

>>>bool(1==1)True

Ocomandobool()nãorecebeapenasexpressões,elepodereceberqualquercoisaevairesponder

setalvaloréconsideradoTrueouFalse:

>>>bool(0)False

>>>bool('')False

>>>bool(None)False

>>>bool(1)True

>>>bool(-100)True

>>>bool(13.5)True

>>>bool('teste')True

>>>bool(True)True

3.8CONSTANTES 23

Page 31: Python e Orientação a Objetos

Repare que a função resulta False em strings vazias, quando um número é zero ou quando é

None.AindanãofalamosoqueoNonerepresenta.ONoneéumvalordotipoNoneType,eéusado

pararepresentaraabstençãodeumvalor-comoquandoumargumentopadrãonãoépassadoparaumafunção(queveremosemoutrocapítulo).

type(None)<class'NoneType'>

Emoutraslinguagensdeprogramação,écomumutilizarapalavraNullpararepresentaraabstençãodevalor.ParaprogramadoresmaisexperientesecomalgumconhecimentoemlinguagenscomoJavaeC#,éimportanteobservarquediferentedoNull,oNoneocupaespaçonamemória,éumobjetocomreferência.

Noexemplo acima foramutilizados trêsoperadoresdiferentesdaqueles jávistos anteriormente: o== (igual),o> (maiordoque)eo< (menor doque).Estes operadores não são aritméticos, são

conhecidosporoperadoresdecomparação.OPythonpossuimaisoperadoresdestetipo:

Operação Descrição

a==b aigualab

a!=b adiferentedeb

a<b amenordoqueb

a>b amaiordoqueb

a<=b amenorouigualab

a>=b amaiorouigualab

Outrosoperadoresqueretornamvaloresbooleanossão:

Operação Descrição

aisb Trueseaebsãoidênticos

aisnotb Trueseaebnãosãoidênticos

ainb Trueseaémembrodeb

anotinb Trueseanãoémembrodeb

Éimportantesaberqueosoperadores==eis funcionamdemaneira diferente.Vamosusar o

exemplodeduaslistasechecarseelassãoiguais:

>>>x=[1,2,3]>>>y=[1,2,3]>>>x==yTrue

>>>xisyFalse

24 3.8CONSTANTES

Page 32: Python e Orientação a Objetos

Ooperador==checaseoconteúdodasvariáveissãoiguaiseseucomportamentopodevariarde

interpretador para interpretador - o exemplo acima é o comportamento padrão do CPython. Já ooperadorischecaseaebsãoomesmoobjeto.Falaremosdeobjetosemumoutrocapítulo,maséimportante ter em mente que tudo em Python é um objeto e cada objeto possui uma referência namemória.Ooperadorisvaichecarexatamentesexeysãoomesmoobjeto,ouseja,sepossuema

mesmareferência.

Esequisermosapresentarumamensagemdiferenteparaousuáriodependendodovalordeentrada?Vamos atribuir um valor para uma variável numero e pedir para o usuário entrar com um valor.

Devemos verificar se os valores são iguais como um jogo de adivinhação em que o usuário deveadivinharonúmerodefinido.

numero=42chute=input('Digiteumnúmero:')

Atéaqui,nenhumanovidade.Agora,devemosmostraramensagem"Vocêacertou"casoonumero

sejaigualaochute,e"Vocêerrou"casoonumerosejadiferentedochute.Emportuguês, seria

assim:

Sechuteigualanúmero:"Vocêacertou"Sechutediferentedenúmero:"Vocêerrou"

Oumelhor:

Sechuteigualanúmero:"Vocêacertou"Senão:"Vocêerrou"

PodemostraduzirissoparacódigoPython.OPythonpossuiooperadorcondicionalpararepresentarapalavrasequeéoifeapalavrasenãoqueéoelse.Asintaxeficaria:

ifchute==numero:print('Vocêacertou')else:print('Vocêerrou')

EstecódigoaindanãofuncionaporqueoPythonentendeasinstruçõesifeelsecomoblocose

osblocosdevemseguirumaindentação.Comoprint('Vocêacertou')éainstruçãoquedeveserexecutadacasoaverificaçãodoif sejaverdadeira,devemos terumrecuoparaadireitaemquatros

espaços:

ifchute==numero:print('Vocêacertou')else:print('Vocêerrou')

Casocontrário,ointerpretadorvaiacusarerrodesintaxe.Dessamaneira,ocódigoficamaislegível

3.9COMANDOIF

3.9COMANDOIF 25

Page 33: Python e Orientação a Objetos

eoqueemoutraslinguagenséumaescolhadoprogramador,oPythonteobrigaafazerforçando,destamaneira, a organizar o código.Tudoque estiver no bloco da primeira condição (doif) deve estar

indentado,ouseja,recuadoparadireita.Assimcomoasinstruçõesqueestiveremnoblocodoelse.

Nofim,nossoprograma,quesalvaremosemumarquivochamadoadivinhacao.pyfica:

numero=42chute=input('Digiteumnúmero:')

ifchute==numero:print('Vocêacertou')else:print('Vocêerrou')

Eexecutamosnoterminal:

dev@caelum:~$python3adivinhacao.pyDigiteumnúmero:25Vocêerrou

Notequeacondiçãodeumifdeveserumbooleano,ouseja,TrueouFalse.Passamosaexpressãochute==numero que vai checar se ela é verdadeira ou não.Caso seja verdadeira, vai executar o

código dentro do bloco do if, senão, vai executar o código dentro do else. Agora, vamos chutar onúmero42everificarsetudoestáfuncionando:

dev@caelum:~$python3adivinhacao.pyDigiteumnúmero:42Vocêerrou

Algodeerradoaconteceu!Digitamosonúmerocorretoemesmoassimoprogramanãofuncionoucomoesperado.Vamosentenderoqueaconteceu.

Afunçãoinput()lêovalordigitadopelousuáriocomoumastring.

chute=input('Digiteumnúmero:')

Seousuáriodigitaronúmero42,avariávelchutevaiguardarovalor"42",ouseja,umtexto.

Podemoschecar issoatravésdafunçãotype()que retornao tipodavariável.Vamos testar issono

terminal:

>>>chute=input('Digiteumnúmero:')Digiteumnúmero:42>>>type(chute)<class'str'>

Agora fica mais claro porque o programa não está funcionando como o esperado. Quando ointerpretadorverificarchute==numero vai retornarFalse já que"42" (texto) é diferente de42

3.10CONVERTENDOUMASTRINGPARAINTEIRO

26 3.10CONVERTENDOUMASTRINGPARAINTEIRO

Page 34: Python e Orientação a Objetos

(número).

Parafuncionar,precisamosconverterastring"42"paraumnúmerointeiro.Ointtambémfuncionacomoumafunção(maisparafrenteentenderemosquenãoérealmenteumafunção)quepodereceberumastringeretornarointeirocorrespondente:

>>>numero_em_texto='12''12'>>>type(numero_em_texto)<class'str'>>>>numero=int(numero_em_texto)12>>>type(numero)<class'int'>

Masdevemostomarcuidado,nemtodastringpodeserconvertidaparaumnúmerointeiro:

>>>texto='caelum'>>>numero=int(texto)Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>ValueError:invalidliteralforint()withbase10:'caelum'

OinterpretadoracusaumValueErrordizendoqueovalorpassadoparaint()éinválido,ouseja,éumtextoquenãorepresentaumnúmerointeiro.

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

Podemosmelhoraraindamaisojogo:casoochutenãosejaigualaonúmerosecreto,podemosdarumapistaparaousuárioseelefoimaioroumenordoqueochuteinicial.Ouseja,devemosacrescentaressetratamentocasoousuárioerreochute:

Sechute=número:"Vocêacertou!"Senão:Sechutemaiordoquenúmero_secreto:"Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto"

VocêpodetambémfazerocursodatadessaapostilanaCaelum

3.11OCOMANDOELIF

3.11OCOMANDOELIF 27

Page 35: Python e Orientação a Objetos

Senão"Vocêerrou!Oseuchutefoimenorqueonúmerosecreto"

JásabemostraduzirissoparaPython:

if(chute==numero_secreto):print('Vocêacertou!')else:if(chute>numero_secreto):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')else:print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

Masnestecasopodemosfazerumelsecomumacondiçãodeentrada,oelif.Vamosutilizá-lo

paradeixarocódigomaissemântico,jáquenapráticanãohádiferença:

if(numero_secreto==chute):print('Vocêacertou!')elif(chute>numero_secreto):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(chute<numero_secreto):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

Podemos melhorar ainda mais a legibilidade do código para que os outros programadores, quepodemajudaradesenvolvê-lonofuturo,entendammelhor.Vamosdeixarnossascondiçõesmaisclaras.chute==numero_secreto quer dizer que o usuário acertou.Então, extraímos essa condição para

umavariável:

acertou=chute==numero_secreto

if(acertou):print('Vocêacertou!')

#restantedocódigo

Avariávelacertouguardaumaexpressãoe,portantoédo tipobooleano epodemosusarcomocondiçãonocomandoif.Agoraacondiçãoifficaumpoucomaisclara.Vamosfazeramesmacoisa

paraasoutrasduascondições:

acertou=chute==numero_secretomaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou!')elif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

1. Crieumarquivochamadoadivinhacao.pyemumapastachamadajogosdentrododiretóriohome:

|_home

3.12EXERCÍCIOS-JOGODAADIVINHAÇÃO

28 3.12EXERCÍCIOS-JOGODAADIVINHAÇÃO

Page 36: Python e Orientação a Objetos

|_jogos|_adivinhacao.py

2. Abra o arquivo no editor de texto de sua preferência e comece a escrever um cabeçalho para ousuáriosaberdoquesetrataoprograma:

print('******************************')print('*Jogodaadivinhação*')print('******************************')

3. Vamosdefiniravariávelnumero_secretoquevaiguardarovaloraseradivinhadopelousuário:

print('******************************')print('*Jogodaadivinhação*')print('******************************')

numero_secreto=42

4. Captureaentradadousuáriousandoafunçãoinput():

print('******************************')print('*Jogodaadivinhação*')print('******************************')

numero_secreto=42

chute=input('Digiteoseunúmero:')print('Vocêdigitou:',chute)

5. Compareovalordigitadopelousuáriocomonumero_secreto.Seosvaloresforemiguais,mostre

umamensagemdeacerto,casocontrário,mostreumamensagemdeerro:

print('******************************')print('*Jogodaadivinhação*')print('******************************')

numero_secreto=42

chute=input('Digiteoseunúmero:')print('Vocêdigitou:',chute)

if(numero_secreto==chute):print('Vocêacertou!')else:print('Vocêerrou!')

6. Rodeocódigoacimapeloterminaletesteojogochutandoonúmero42:

dev@caelum:~$python3jogos/adivinhacao.py

7. O chute 42não funciona comoesperado.Esquecemosde converter o chute digitadopelousuárioparaumnúmero inteiro.Modifiqueocódigoeutilizea funçãoint()para receberaentradado

usuário:

chute=int(input('Digiteoseunúmero:'))

8. Rodeocódigonovamentecomaentradaiguala42evejaqueagorafuncionacomoesperado.

3.12EXERCÍCIOS-JOGODAADIVINHAÇÃO 29

Page 37: Python e Orientação a Objetos

9. Vamosapresentarumapistaparaousuárioeimprimirumamensagemdizendoseochutefoimaioroumenordoqueonúmerosecreto.Paraissousaremosoelif:

if(numero_secreto==chute):print('Vocêacertou!')elif(chute>numero_secreto):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(chute<numero_secreto):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

10. Agoravamosmelhoraralegibilidadedocódigoextraindoascondiçõesparavariáveis:

acertou=chute==numero_secretomaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou!')elif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

11. Rodeoprogramaetestecomtodasassituaçõespossíveis.

Queremosdarmaisdeumaoportunidadeparaousuáriotentaracertaronúmerosecreto,jáqueéumjogo de adivinhação.A primeira ideia é repetir o código, desde a função input() até o bloco do

elif. Ou seja, para cada nova tentativa que quisermos dar ao usuário, copiaríamos esse código

novamente.

Sóque copiar código sempre éumamáprática, queremos escrevero código apenasumavez.Sequeremosrepetirocódigo,fazemosumlaço,ouumloop,quedeverepetirainstruçãodentrodeblocoenquantoelaforverdadeira.Olaçoquedevemosfazeré:

Enquantoaindahátentativas,faça:chute_str=input('Digiteoseunúmero:')print('Vocêdigitou:',chute_str)chute=int(chute_str)

acertou=numero_secreto==chutemaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou!')elif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

print('FimdoJogo!')

3.13COMANDOWHILE

30 3.13COMANDOWHILE

Page 38: Python e Orientação a Objetos

Comoditoanteriormente,oPythonnãoentendeportuguêseassimcomooifeletemumcomando

quesubstituiráapalavraenquantodonossoexemplo.Owhile é essecomandoque, assimcomooif,recebeumacondição.Adiferençaéqueoif,casoacondiçãosejaverdadeira,executaapenas

umavezocódigodeseubloco,jáowhileexecutaenquantoacondiçãoforverdadeira,porexemplo:

x=5enquantoxformaiordoque1,faça:imprime(x)x=x-1

QueemPython,éequivalentea:

>>>x=5>>>while(x>1):...print(x)...x=x-15432

Mastomecuidado,oqueaconteceseesquecermosessalinhadocódigox=x-1?

>>>x=5>>>while(x>1):...print(x)55555...

Oprogramavaiimprimironúmero5infinitamente,jáqueacondiçãopassadaésempreverdadeiraenãomudadentrodobloco.

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Seuslivrosdetecnologiaparecemdoséculopassado?

3.13COMANDOWHILE 31

Page 39: Python e Orientação a Objetos

1. Daremosaousuáriodo jogoumnúmeromáximode tentativas.Abraoarquivoadivinhacao.py napasta jogos e atribua a variável total_de_tentativas com valor 3, e acrescente o bloco docomandowhile:

numero_secreto=42total_de_tentativas=3

while(aindahátotal_de_tentativas):#executaocódigo

2. Restaagoraaexpressãoaindahá.Aideiaéqueousuáriotenha3tentativas,representadanocódigopelavariáveltotal_de_tentativas.A cada rodada subtraímos1dovalor dessavariável, até o

valor chegar a 0, que é quando devemos sair do while. Logo, vamos executá-lo enquanto a

variáveltotal_de_tentativasformaiordoque0:

numero_secreto=42total_de_tentativas=3

while(total_de_tentativas>0):chute=int(input('Digiteoseunúmero:'))print('Vocêdigitou:',chute)

acertou=chute==numero_secretomaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou')elif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

total_de_tentativas=total_de_tentativas-1

OBS:Nãoesqueçadeindentarocódigodentrodoblocowhileparanãorecebererrodesintaxe.

3. Alémdastentativas,podemosapresentarqualonúmerodarodadaqueousuárioestájogandoparadeixarclaroquantastentativaseletêm.Paraissovamoscriaravariávelrodada,quecomeçacomo

valor1:

total_de_tentativas=3rodada=1

4. Evamosimprimi-laantesdousuáriodigitaroseuchute:

total_de_tentativas=3rodada=1

while(total_de_tentativas>0):print('Tentativa{}de{}'.format(rodada,total_de_tentativas))chute=int(input('Digiteoseunúmero:'))print('Vocêdigitou:',chute)

3.14EXERCÍCIOS-JOGOCOMWHILE

32 3.14EXERCÍCIOS-JOGOCOMWHILE

Page 40: Python e Orientação a Objetos

#restantedocódigoaqui

5. Eparaavariáveltotal_de_tentativascontinuarcomovalor3,nãovamosmaissubtrair1do

seuvalor,esimadicionar1aovalordavariávelrodada:

total_de_tentativas=3rodada=1

while(total_de_tentativas>0):print('Tentativa{}de{}'.format(rodada,total_de_tentativas))

chute=int(input('Digiteoseunúmero:'))print('Vocêdigitou:',chute)

acertou=numero_secreto==chutemaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou!')elif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

rodada=rodada+1

print('Fimdojogo')

6. Por fim,precisamosmodificar a condição. Jáqueototal_de_tentativas permanecerá como

valor3,ocódigoprecisaficarexecutandoenquantoovalordarodadaformenorouigualaototaldetentativas:

total_de_tentativas=3rodada=1

while(rodada<=total_de_tentativas):print('Tentativa{}de{}'.format(rodada,total_de_tentativas))chute_str=input('Digiteoseunúmero:')

#restantedocódigo

Agoraconseguimosimprimirparaousuárioquantastentativasrestanteselepossui!Testechamandoseuarquivoadivinhacao.pycomocomando'python3'.

7. Falta arrumar uma coisa: quando o usuário acerta, o jogo continua pedindo um novo chute.Queremosterminaraexecuçãodoprogramaquandoousuárioacerta.Paraissousamosocomandobreak.Quandoointerpretadorencontrarocomandobreakeleparaaexecuçãodoprograma.vamosacrescentar issoquandoousuário acertar, ou seja, noprimeiro comandoif após a exibição da

mensagemdeacerto:

if(acertou):print('Vocêacertou!')breakelif(maior):

3.14EXERCÍCIOS-JOGOCOMWHILE 33

Page 41: Python e Orientação a Objetos

#restantedocódigo

8. Testeoprogramaevejasetudoestáfuncionandocomooesperado.

Ainda no código do jogo da adivinhação, implementamos o loop while, no qual temos uma

variávelrodadaquecomeçacomovalor1,eéincrementadadentrodoloop,queporsuaveztemuma

condiçãodeentrada,queéarodadasermenorouigualaototaldetentativas,queé3.

Ouseja,arodadatemumvalorinicial,queé1,evaiaté3.Fazemosumlaçocomeçandocomum

valorinicial,atéumvalorfinal,sempreincrementandoessevaloracadaiteração.Masseesquecermosdeincrementararodada,entramosemumloopinfinito.

Emcasos comoesse, existe umoutro loop que simplifica essa ideia de começar comumvalor eincrementá-loatéchegaremumvalorfinal:oloopfor.

Paraentenderoloop,oulaçofor,podemosiratéoconsoledoPythonparaveroseufuncionamento.Aideiaédefinirmosovalorinicialeovalorfinal,queoloopoincrementaautomaticamente.Paraistoutilizamosafunçãoembutidarange(),passando-osporparâmetro,definindoassimasériedevalores.

Asintaxeéaseguinte:

Paravariávelemumasériedevalores:Façaalgo

Isso,emPython,podeficarassim:

forrodadainrange(1,10):

Orange(1,10)vaigerarointervalodenúmerosinteirosde1a9.Naprimeiraiteração,ovalor

davariávelrodadaserá1,depois2eatéchegaraovalorfinaldafunçãorange()menos1,istoé,o

segundoparâmetrodafunçãonãoéinclusivo.Noexemploacima,asériedevaloreséde1a9.PodemosconfirmarissoimprimindoovalordavariávelrodadanoconsoledoPython:

>>>forrodadainrange(1,10):...print(rodada)...123456789

Comafunçãorange(),podemosdefinirumstep(umpasso),queéointervaloentreoselementos.

Porpadrão,osteptemvaloriguala1,maspodemosalterarestevalorpassandoumterceiroparâmetro

3.15COMANDOFOR

34 3.15COMANDOFOR

Page 42: Python e Orientação a Objetos

paraafunção:

>>>forrodadainrange(1,10,2):...print(rodada)...13579

Vejaqueointervaloentrecadaelementodasérieagoraé2,acadaiteraçãoolaçopuladoispassos(incrementa2).Masnãonecessariamenteprecisamosusarafunçãorange()nofor,podemospassar

osvaloresdasequênciamanualmenteconseguindoomesmoresultado:

>>>forrodadain[1,2,3,4,5]:...print(rodada)...12345

Tantoowhilequantooforpodemserusadosnojogo.Conseguiremosomesmoresultado,mas

o código ficamais verboso comowhile, alémde corrermoso riscode esquecer de incrementar a

rodada(rodada=rodada+1)enossocódigoentraremumloopinfinito.Nestecasos,épreferível

utilizarocomandofor.

1. Substituaocomandowhilepeloforcomeçandono1eindoatéototal_de_tentativas.Não

esqueçaderemoveradeclaraçãodavariávelrodadaeoseuincrementodentrodoloop:

numero_secreto=42total_de_tentativas=3

forrodadainrange(1,total_de_tentativas):print('Tentativa{}de{}'.format(rodada,total_de_tentativas))

chute=int(input('Digiteoseunúmero:'))print('Vocêdigitou:',chute)

acertou=numero_secreto==chutemaior=chute>numero_secretomenor=chute<numero_secreto

if(acertou):print('Vocêacertou!')breakelif(maior):print('Vocêerrou!Oseuchutefoimaiorqueonúmerosecreto')elif(menor):print('Vocêerrou!Oseuchutefoimenorqueonúmerosecreto')

3.16EXERCÍCIOS-UTILIZANDOOFORNOJOGO

3.16EXERCÍCIOS-UTILIZANDOOFORNOJOGO 35

Page 43: Python e Orientação a Objetos

print('Fimdojogo!')

2. Éimportantesaberqueofornãoéobrigadoaterparênteses.Podemostestareverqueoprograma

dáapenas2tentativas.Issoporque,comofoifaladoanteriormente,osegundoparâmetrodafunçãorange não é inclusivo, no caso do nosso jogo, range(1,3) irá gerar a série 1 e 2 somente.

Portanto,vamossomar1aototal_de_tentativasdentrodafunçãorange:

forrodadainrange(1,total_de_tentativas+1):

3. Testenovamenteojogoevejaquetudoestáfuncionandoperfeitamente!

4. (opcional)Crieumníveldedificuldadeparaojogo.Crieumavariávelchamadanívelepeçapara

ousuárioescolheremqualníveleledeseja jogar.Onívelémensuráveldeacordocomo totaldetentativas:nível1(tentativas=20),nível2(tentativas=10)enível3(tentativas=5).

5. (opcional) Acrescente um total de pontos ao jogador que deve iniciar com 1000 e a cada chuteerradodevesersubtraídodototaldepontosumvalorquecorrespondeadiferençaentreochuteeonúmerosecreto.Paraesteexercíciovocêvaiprecisardafunçãoabs().Vejanadocumentaçãodo

Pythoncomoelafunciona.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Agoraéamelhorhoradeaprenderalgonovo

36 3.16EXERCÍCIOS-UTILIZANDOOFORNOJOGO

Page 44: Python e Orientação a Objetos

CAPÍTULO4

Aoadentrarmosemcódigosquerequeremmaiorcomplexidade,vemosquenemsempreomodoscriptde execução de códigos em Python não conseguirá atender todas as nossas necessidades durante acriaçãodenossasaplicações.Dessaforma,seránecessárionosaprofundarmosaescrevernossocódigoemarquivosexternos,demaneiraaexecutartodoocódigodeumavezdemaneiramaisestruturadaeorganizada.

Aoiniciaroprimeirocontatocomprogramação,umadasprincipaisdúvidasde iniciantesé:"qualferramenta vou utilizar para escrever código?". A maioria dos códigos das principais linguagens deprogramaçãopermitemdesenvolveremumarquivoutilizandoumeditordetextocomum.

Algunseditoresde textopossuemferramentasmais sofisticadasquedãomaiorauxílionahoradedesenvolver como: indentação de código, diferenciação de funções, autocompletamento de código,dentreoutras.

Outraferramenta,maisutilizadaparadesenvolvercódigo,éoquechamamosdeAmbienteIntegradodeDesenvolvimentoouIDE(siglaeminglêsparaIntegratedDevelopmentEnviroment).UmaIDEéumsoftware commuitas funcionalidades que auxiliam no desenvolvimento de código além de possuir acapacidadederodarocódigo.

Nestecapítulo,apresentaremosaIDEPycharmesuasprincipaisferramentas.

INTRODUÇÃOAOPYCHARM

4.1IDE

4INTRODUÇÃOAOPYCHARM 37

Page 45: Python e Orientação a Objetos

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

O Pycharm é multiplataforma com versões para Windows, MacOS e Linux. O PyCharm édesenvolvidopelaempresaJetBrainseforneceanálisedecódigo,depuradorgráfico,autocompletamentodecódigoecapacidadesdenavegaçãoquefacilitamaescritadecódigo.

IDE's foram desenvolvidas para criar códigomais rapidamente e commaior eficiência. VeremosaquiosprincipaisrecursosdoPyCharm.Vocêperceberáqueeleevitaaomáximoatrapalhareapenasgeratrechosdecódigosóbvios,sempreaoseucomando.

Nositeoficialháguiasetutoriaisparainiciantes.Sevocêseinteressar,recomendamosusaroguiainicialnestelink:https://www.jetbrains.com/help/pycharm/meet-pycharm.html

Com o PyCharm você pode desenvolver em Python. A versão Profissional dá suporte paradesenvolvimentodeaplicaçõeswebcomDjango,FlaskePyramid.OPycharmtambémsuportaHTML,CSS, JavaScript e XML. Suporte para outras linguagens também podem ser adicionadas baixandoplugins.

EditoraCasadoCódigocomlivrosdeumaformadiferente

4.2PYCHARM

38 4.2PYCHARM

Page 46: Python e Orientação a Objetos

OUTRASIDES

UmanovatendênciadeIDE'sparadesenvolvimentoemPython,especialmenteparaaáreadeDataScience,envolvemautilizaçãodeambientescomputacionaisdiretamenteutilizadosnaweb,possibilitandoumainteraçãomaisdinâmicaecolaborativaentreosseusparceirosdeequipe.Sendomuitas vezes definidos como Notebooks, os mesmos possibilitam a execução de scripts em

Pythonemblocosseparadosdecódigo,facilitandoavisualizaçãoedocumentaçãodoseucódigo,eobservandoosresultadosemtemporealcommaioreficácia.UmdosexemplosmaisfamososéoJupyterNotebook,quepossuisuporteadiversasbibliotecaseédefácilinstalaçãodoambientenasuamáquina,sendoutilizadopordiversasempresasnoramodedadosatéhoje.

OutraIDEqueestácadavezmaisdisseminadanaatualidadeparaageraçãodecódigosPythonéoGoogleColaboratory,quepossibilitaacriaçãodeNotebooksinterativosdiretamenteatravés

desuacontaGoogle.UmadasmaioresvantagensdoColaboratoryaoutrasIDE'séquetudoéfeitoatravés da nuvem, desde o armazenamento dos dados dentro da sua conta Google até oprocessamentodedadosfeitopelaGPUfornecidapelamesma.

Porfim,umoutroambientebastanteconsolidadonacomunidadePythonnosúltimosanoséoIDLE, que possui bem menos recursos do que o PyCharm mas ainda é bastante utilizado pordiversosusuários.

No site oficial da Python Brasil existem uma lista imensa de outras IDEs:https://wiki.python.org.br/IdesPython.

Se você ainda não possui o PyCharm, faça o download nesta página:https://www.jetbrains.com/pycharm/.

4.3DOWNLOADEINSTALAÇÃODOPYCHARM

4.3DOWNLOADEINSTALAÇÃODOPYCHARM 39

Page 47: Python e Orientação a Objetos

Existem duas versões, aProfessional e aCommmunity. A versão paga (aProfessional) possuifuncionalidadesextrascomosuporteparadesenvolvimentodeaplicaçõeswebeintegraçãocombancode dados. Para o curso, a versãoCommunity será suficiente. Para o download, siga as instruçõesdependendodeseusistemaoperacional.

Se você precisar de ajuda para fazer a instalação, consulte as instruções de instação neste link:https://www.jetbrains.com/help/pycharm/install-and-set-up-pycharm.html

AoabriroPyCharmpelaprimeiravez,uma janelachamadaCreateProject aparecerá.Énela

quedefinimostodasasconfiguraçõesnecessárias.

4.4CRIANDOUMPROJETO

40 4.4CRIANDOUMPROJETO

Page 48: Python e Orientação a Objetos

Podemoscriarumnovoprojetoaqualquermomento.Parafazerisso,bastaclicaremFile->New

ProjectnomenusuperiordajanelaprincipaldoPyCharm.

Primeiro,especificamosonomedoprojeto-nonossocasoseráapenasjogos.NotequeoPyCharmsugereumlocalpadrãoparasalvaroprojeto.VocêpodeaceitarestelocalouconfigurarmanualmentenocampoLocation.Vamosoptarpelocaminhopadrão.Aofazerisso,aIDEvaicriarumapastachamada

PyCharmProjectsnasuapastahome.

Após isso, escolhemos a versão do interpretador que usaremos no projeto. O PyCharm cria umambienteisoladodainstalaçãopadrãodosistemaoperacional(nocasodoLinuxeMacOS).Issoémuitoimportante e não causa concorrência com outras bibliotecas instaladas em seu computador. Por fim,clicamosemCreateenossoprojetoécriado.

Nosso projeto tem uma estrutura padrão. A pasta venv é o ambiente isolado do sistema

operacional.Nela contémaversãodo interpretadorPythonque selecionamosna criaçãodoprojeto eseus módulos embutidos (builtins) - você pode checar isso na pasta lib . A qualquer momento

podemosincluirnovasbibliotecasaoprojeto.

Porfim,vamosimportaronossoúltimocódigogeradonocapítuloanterior,oadivinhacao.py.Vá

naabaFile->Openeselecioneoarquivoparaoseuprojeto.

4.4CRIANDOUMPROJETO 41

Page 49: Python e Orientação a Objetos

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

Agoravamostestarnossaclasse.OPycharmpossuiumconsoledoPythonembutido,paraabrí-lováemTools->PythonConsole.Vocêvainotarquea janeladoconsolevaiabrirabaixodoarquivo

adivinhacao.py:

Reparequeoconsoletambémpossuiaferramentadeautocomplete.Parareiniciá-lobastaclicarnoprimeiroíconedomenuesquerdodoconsole.

AIDEtambémpermiteabriroterminaleusaromodointerativoparatestes.OatalhoparaabriroterminaléALT+F12.OPythonConsoledoPycharmémaisaconselhávelpara issoeo terminalé

maisutilizadoparainstalarnovaslibsaoseuprojeto.

ParaexecutarváemRun->Runoucliquecomobotãodireitodomousenointeriordoarquivo

adivinhacaoeescolhaaopçãoRun'adivinhacao'.Ouainda,digiteoatalhoCTRL+Shift+F10que

vaiteromesmoefeito.Depoisdeterrodadopelaprimeiravez,paravocêrodarnovamentebastaclicarnoíconedeumapequenasetaverdenomenusuperiordaIDE:

JáconheceoscursosonlineAlura?

4.5EXECUTANDOCÓDIGO

42 4.5EXECUTANDOCÓDIGO

Page 50: Python e Orientação a Objetos

OPycharmpossuimuitosatalhosúteisnahoradedesenvolver.Abaixoestãoosprincipaisdelesparavocêconhecerepraticar:

ALT+INSERT:crianovoarquivo

ALT+ENTER:sugereaçõeseconsertaerrosrápidos

CTRL+ESPAÇO:autocomplete

CTRL+N:buscaclasses

CTRL+SHIFT+N:buscaarquivos

CTRL+ALT+M:extraircódigoparaummétodo

CTRL+ALT+V:extrairparaumavariável

CTRL+ALT+SHIFT+T:refatoração(renomear)

CTRL+A:selecionatudo

CTRL+SHIFT+LEFT/RIGHT:selecionapartedotextoaesquerdaouadireita.

CTRL+SHIFT+PAGEDOWN/PAGEUP-movelinhaparacimaouparabaixo

ALT+J:procurapróximapalavraselecionada

A JetBrains fez uma tabela com todos os atalhos que você pode checar neste link:resources.jetbrains.com/storage/products/pycharm/docs/PyCharm_ReferenceCard.pdf

4.6PRINCIPAISATALHOS

4.6PRINCIPAISATALHOS 43

Page 51: Python e Orientação a Objetos

CAPÍTULO5

Nocapítulopassadocriamosojogodaadivinhação,agoravamoscriarojogodaForca.Vamoscomeçarcomos conhecimentos que temos até aqui para estruturarmos o nosso jogo.Vamos criar um arquivochamadoforca.pynapastajogos:

|_home|_jogos|_adivinhacao.py|_forca.py

Comonojogodaadivinhação,devemosadivinharumapalavrasecreta,entãonadamais justoquedefini-laemumavariável.Porenquanto,apalavraseráfixa,comovalorbanana:

print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

palavra_secreta='banana'

print('Fimdojogo')

Maisàfrentedeixaremosessapalavrasecretamaisdinâmica.

Como estamos tratando de um jogo da forca, o usuário deve acertar uma palavra e chutar letras.Além disso, precisa saber se o usuário acertou ou errou e saber se foi enforcado ou não. Entãoprecisaremosde2variáveisbooleanasenforcoueacertouparaguardaresta informaçãoeo jogo

continuaráatéumadelasforTrue;paratalacrescentamosumlaçowhile:

acertou=Falseenforcou=False

while(notacertouandnotenforcou):print('Jogando...')

Ousuáriodojogotambémvaichutarletras.Alémdisso,seochuteforigualaumaletracontidanapalavra_secreta,querdizerqueousuárioencontrouumaletra.Podemosutilizarumlaçoforpara

tratarisso,assimcomofizemosnojogodaadivinhaçãoparaapresentarastentativaserodadas:

while(notacertouandnoterrou):chute=input('Qualletra?')

posicao=0forletrainpalavra_secreta:if(chute==letra):print('Encontreialetra{}naposição{}'.format(letra,posicao))

ESTRUTURADEDADOS

44 5ESTRUTURADEDADOS

Page 52: Python e Orientação a Objetos

posicao=posicao+1

print('Jogando...')

Comoumastringéumasequênciadeletras,oloopforvaiiterarporcadaletra.

Nossapalavra_secretaé'banana'.Eseousuáriochutaraletra'A'aoinvésde'a'?Vamostestar:

>>>$python3jogos/forca.py************************************BemvindoaojogodaForca!************************************Qualletra?AJogando...Qualletra?

OPythonécase-sensitive,ouseja'a'e'A'sãodistintosparaointerpretador.Seráprecisoacrescentarestetratamentoefazerojogoaceitarcomoacertotanto'a'como'A'.String(str)éumtipoembutido

noPythonquepossuialgumasfunçõesprontaseumadelasvainosajudarnesteproblema.

Existe uma função chamada upper() que devolve uma string com todas as letras maiúsculas.

Tambémpossuialower()quedevolveumastringcomtodasasletrasminúsculas:

>>>texto='python'>>>texto.upper()'PYTHON'>>>>>>texto='PYTHON'>>>texto.lower()'python'

Agora precisamos modificar a condição chute == letra do if para chute.upper() ==

letra.upper():

if(chute.upper()==letra.upper()):print('Encontreialetra{}naposição{}'.format(letra,posicao))

Dessamaneira, o jogador pode chutar tanto 'a' como 'A' queo tratamentodoif vai considerar

todascomomaiúsculas.

Agorapodemostestarnovamentecomchuteiguala'A'(ou'a')evemosoprogramafuncionarcomooesperado:

************************************BemvindoaojogodaForca!************************************Qualletra?AEncontreialetraanaposição1Encontreialetraanaposição3Encontreialetraanaposição5Jogando...Qualletra?

Atualmente, jádizemosao jogadoremqueposiçãoa letraqueelechutouestánapalavra secreta,casoaletraexistanapalavra.Masemumjogorealdeforca,ojogadorvêquantasletrashánapalavra

5ESTRUTURADEDADOS 45

Page 53: Python e Orientação a Objetos

secreta.Algocomo:

Qualletra?______

Eseeleencontraralgumaletra,amesmatemasualacunapreenchida.Aodigitaraletra"a",ficaria:

_a_a_a

Muito mais intuitivo, não? Vamos implementar essa funcionalidade. Para exibir as letras dessaforma,precisamosguardaroschutescertosdousuário,mascomofazerisso?

Paratal,oPythonnosofereceumtipodeestruturadedadosquenospermiteguardarmaisdeumvalor.Essaestruturaéalist(lista).Paracriarumalista,utilizamoscolchetes([]):

>>>valores=[]>>>type(valores)<class'list'>

Assimcomoastring,list tambéméuma sequênciadedados.Podemosver suadocumentação

atravésdafunçãohelp():

>>>help(list)Helponclasslistinmodulebuiltins:classlist(object)|list()->newemptylist|list(iterable)->newlistinitializedfromiterable'sitems||Methodsdefinedhere:||__add__(self,value,/)|Returnself+value.||__contains__(self,key,/)|Returnkeyinself.||__delitem__(self,key,/)|Deleteself[key].||__eq__(self,value,/)|Returnself==value.||__ge__(self,value,/)|Returnself>=value.

#códigoomitido

Vemosoquepodemos fazercomuma lista.Podemos,porexemplo,verificaro seuvalormínimocommineoseumáximocommax.Nossalistaaindaestávazia,masjápodemosiniciá-lacomalguns

valoreseutilizarmosessasfunçõesparaverificarmosseusvaloresmáximoemínimo:

>>>valores=[0,1,2,3]>>>min(valores)0>>>max(valores)3

Para acessar um valor específico, podemos acessá-lo através do seu índice (posição).O primeiro

46 5ESTRUTURADEDADOS

Page 54: Python e Orientação a Objetos

elementodalistapossuiíndice0,osegundopossuiíndice1eassimpordiante:

>>>valores=[0,1,2,3]>>>valores[2]2>>>valores[0]0

Paramodificarumvalor,bastausarooperadordeatribuiçãoemumadeterminadaposição:

>>>valores[0]=4>>>valores[4,1,2,3]

É possível saber o tamanho da lista com a função len e verificar se determinado valor está

guardadonelacomocomandoin:

>>>valores=[0,1,2,3]>>>len(valores)4>>>0invaloresTrue>>>6invaloresFalse

Além disso, existem funções específicas da lista, que podem ser acessadas na documentação:https://docs.python.org/3.6/library/stdtypes.html#mutable-sequence-types.

Podemos adicionar elementos ao final da lista com a função append(), exibir e remover um

elementodedeterminadaposiçãocomafunçãopop(),entrediversasoutrasfuncionalidades.

Agoraquesabemoscomoguardarvaloresemumalista,podemosvoltaraonossojogoeguardarosacertosdousuário.Comoqueremosexibirosespaçosvaziosprimeiro,criaremosumalistacomeles,namesmaquantidadedeletrasdapalavrasecreta:

palavra_secreta='banana'letras_acertadas=['_','_','_','_','_','_']

Já temos a posição da letra (também chamado de índice). Logo, caso o chute seja correto, bastaguardaraletradentrodalista,nasuaposiçãocorretaeimprimiralistaapósolaçofor:

posicao=0

forletrainpalavra_secreta:if(chute.upper()==letra.upper()):letras_acertadas[posicao]=letraposicao=posicao+1

print(letras_acertadas)

Ouseja,paracadaletranapalavrasecreta,oprogramavaiverificarsechuteéigualaletra.Em

casoafirmativo,adicionaaletranaposiçãocorretaeincrementaaposicaoapósoblocodoif.

Aoexecutarojogoechutaralgumasletras,temos:

5ESTRUTURADEDADOS 47

Page 55: Python e Orientação a Objetos

$python3jogos/forca.py************************************BemvindoaojogodaForca!************************************Qualletra?b['b','_','_','_','_','_']Jogando...Qualletra?a['b','a','_','a','_','a']Jogando...Qualletra?

Asaídaaindanãoestávisualmenteagradável.Paraficaraindamelhor,vamosexibiralistanoiníciodojogotambémeexcluiroprint('Jogando...')paraocódigoficarmaislimpo.

print(letras_acertadas)

while(notacertouandnoterrou):chute=input('Qualletra?')

#códigoomitido

Nesteexercício,vamosaproveitarnossonovoconhecimentoemlistasparafazercomqueojogodaforcaselembredasletrasacertadaspelojogador.

1. Crieumarquivochamadoforca.pynapastajogos:

|_home|_jogos|_adivinhacao.py|_forca.py

2. Primeiro, precisamosmostrarparao jogador amensagemde abertura, similar aoque foi feitonojogodaadivinhação:

print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

3. Crieavariávelpalavra_secreta,queseráiniciadacomovalor'banana',eumalistapararepresentarasletrasacertadas.

palavra_secreta='banana'letras_acertadas=['_','_','_','_','_','_']

4. Crieasvariáveisbooleanasacertoueerrou,quevamosutilizarnolaçowhile.Alémdisso,

crieavariávelerros,quearmazenaráonumerodeerrosdousuário:

acertou=Falseenforcou=Falseerros=0

while(notacertouandnotenforcou):#código

5.1EXERCÍCIOS:JOGODAFORCA

48 5.1EXERCÍCIOS:JOGODAFORCA

Page 56: Python e Orientação a Objetos

5. Crieavariávelchute,quevaiguardarentradadousuário.

while(notacertouandnotenforcou):chute=input('Qualletra?')

6. Dentrodowhile,crieumloopforparachecarsealetraexistenapalavrasecreta.Seochutefor

igualaletradigitadapelojogador,adicionealetranalistaletras_acertadasnaposiçãocorreta

while(notacertouandnotenforcou):chute=input('Qualletra?')

posicao=0forletrainpalavra_secreta:if(chute==letra):letras_acertadas[posicao]=letraposicao+=1

7. Modifique a condição do if para considerarmos apenas letras maiúsculas utilizando a função

upperdestrings.Atualizetambémavariávelchute.

if(chute.upper()==letra.upper()):

8. Agoraprecisamosincrementaravariávelerroscasoojogadornãoacertealetra.Seochuteéumaletradentrodepalavra_secreta,querdizerqueojogadoracertou,ecasocontrário,incrementamosavariávelerros.Paraisso,teremosmaisumblocoif/else:

if(chuteinpalavra_secreta):posicao=0forletrainpalavra_secreta:if(chute.upper()==letra.upper()):letras_acertadas[posicao]=letraposicao+=1else:erros+=1

9. Atualizeavariávelacertou.Setodasasletrasaindanãoforamacertadas,querdizerqueousuário

aindanãofinalizouojogo,ouseja, letrasacertasaindacontémespaçosvazios('').Dentrodo loopwhile,apósocomandofor,vamosatualizaravariávelacertoueutilizarooperadornotin:

acertou='_'notinletras_acertadas.

Podemoslerocódigoacimacomo"'_'nãoestácontidoemletras_acertadas".

10. Vamostambématualizaravariávelenforcou.Vamosconsiderarqueseojogadorerrar7vezesele

perdeojogo.Comoavariável iniciacom0(zero),querdizerquequandoelafor iguala6,eleseenforca.Atualizeavariáveldentrodoloopwhileapósavariávelacertou:

acertou="_"notinletras_acertadasenforcou=erros==6

11. Para que o jogador acompanhe o resultado a cada chute que ele der, ao final do laço while,

imprimatambémalistaletras_acertadasparaqueelevejacomoeleestáindonojogo:

5.1EXERCÍCIOS:JOGODAFORCA 49

Page 57: Python e Orientação a Objetos

while(notenforcouandnotacertou):

#códigoomitido

print(letras_acertadas)

12. Eclaro,paradarumadicaaonossojogadordequantasletrasapalavratem,vamoscolocaracimadowhileumprintinicialparaqueelevejadeinícioqualotamanhodapalavra:

print(letras_acertadas)

while(notacertouandnotenforcou):...

13. Porfim,vamosimprimirumamensagemde"Vocêganhou!"seousuárioadivinharaletrae"Vocêperdeu"casotenhacometido6erros.Apósolaçowhile,foradele,acrescente:

if(acertou):print('Vocêganhou!!')else:print('Vocêperdeu!!')

print('Fimdojogo')

14. Façaotesteevejanarespostaseseucódigofuncionando.

$python3jogos/forca.py

Nofinal,seucódigodeveestarparecidocomeste:

defjogar():print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

palavra_secreta='banana'letras_acertadas=['_','_','_','_','_','_']

enforcou=Falseacertou=Falseerros=0

print(letras_acertadas)

while(notenforcouandnotacertou):

chute=input("Qualletra?")

if(chuteinpalavra_secreta):posicao=0forletrainpalavra_secreta:if(chute.upper()==letra.upper()):letras_acertadas[posicao]=letraposicao=posicao+1else:erros+=1

enforcou=erros==6acertou='_'notinletras_acertadasprint(letras_acertadas)

50 5.1EXERCÍCIOS:JOGODAFORCA

Page 58: Python e Orientação a Objetos

if(acertou):print('Vocêganhou!!')else:print('Vocêperdeu!!')print('Fimdojogo')

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

Desenvolvemosumnovojogoeconhecemosumanovaestrutura,aslistas.Umalistaédenominadapor uma sequência de valores, e o Python possui outros tipos de dados que também são sequências.Nestemomento,conheceremosumpoucodecadaumadelas.

Sequênciassãocontainers,umtipodedadoquecontémoutrosdados.Existemtrêstiposbásicosdesequência:list (lista) , tuple (tupla) e range (objeto de intervalo). Outro tipo de sequência

famosoquejávimossãoasstringsquesãosequênciasdetexto.

Sequências podem ser mutáveis ou imutáveis. Sequências imutáveis não podem ter seus valoresmodificados. Tuplas, strings e ranges são sequências imutáveis, enquanto listas são sequênciasmutáveis.

As operações na tabela a seguir são suportadas pela maioria dos tipos de sequência, mutáveis eimutáveis.Natabelaabaixo,setsãosequênciasdomesmotipo,n,i,jeksãointeirosexéumobjetoarbitrárioqueatendeaqualquertipoerestriçõesdevalorimpostaspors.

Operação Resultado

xins Trueseumitemdeséigualax

xnotins Falseseumitemdeséigualax

s+t Concatenaçãodeses

snouns Equivalenteaadicionarsasimesmonvezes

s[i] Elementonaposiçãoides

VocêpodetambémfazerocursodatadessaapostilanaCaelum

5.2SEQUÊNCIAS

5.2SEQUÊNCIAS 51

Page 59: Python e Orientação a Objetos

s[i:j] Fatiasdeiparaj

s[i:j:k] Fatiasdeiparajcomopassok

len(s) Comprimentodes

min(s) Menoritemdes

max(s) Maioritemdes

s.count(x) Númerototaldeocorrênciasdexems

Listas

Umalistaéumasequênciadevaloresondecadavaloréidentificadoporumíndiceiniciadopor0.Sãosimilaresastrings(coleçãodecaracteres)excetopelofatodequeoselementosdeumalistapodemserdequalquertipo.Asintaxeésimples,listassãodelimitadasporcolcheteseseuselementosseparadosporvírgula:

>>>lista1=[1,2,3,4]>>>lista1[1,2,3,4]>>>>>>lista2=['python','java','c#']>>>lista2['python','java','c#']

O primeiro exemplo é uma lista com 4 inteiros, o segundo é uma lista contendo três strings.Contudo,listasnãoprecisamconterelementosdemesmotipo.Podemosterlistasheterogêneas:

>>>lista=[1,2,'python',3.5,'java']>>>lista[1,2,'python',3.5,'java']

Nossalistapossuielementosdotipoint,floatestr.Sequeremosselecionarumelemento

específico,utilizamosooperador[]passandoaposição:

>>>lista=[1,2,3,4]>>>lista[0]1>>>lista[1]2>>>lista[2]3>>>lista[3]4>>>lista[4]Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>IndexError:listindexoutofrange

Quandotentamosacessaraposição4fazendolista[4],apareceoerroIndexError,dizendoque

alistaexcedeuseulimitejáquenãoháumquintoelemento(índice4).

OPythonpermitepassarvaloresnegativoscomoíndicequevaidevolverovalornaquelaposiçãode

52 5.2SEQUÊNCIAS

Page 60: Python e Orientação a Objetos

formareversa:

>>>lista=[1,2,3,4]>>>lista[-1]4>>>lista[-2]3

Também é possível usar a função list() para criar uma lista passando um tipo que pode ser

iterávelcomoumastring:

>>>lista=list('python')>>>lista['p','y','t','h','o','n']

Listassãomuitoúteis,porexemplo:

meses=['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro']n=1

while(n<4):mes=int(input("Escolhaummês(1-12):"))if1<=mes<13:print('Omêsé{}'.format(meses[mes-1]))n+=1

Etestamos:

>>>Escolhaummês(1-12):4OmêséAbril>>>Escolhaummês(1-12):11OmêséNovembro>>>Escolhaummês(1-12):6OmêséJunho

Primeiro criamos um lista, relacionamos seus índices com os meses do ano, recuperamos seusvaloresatravésdaentradadousuárioe imprimimosna telaomêsescolhido(mes[mes-1]) indexadoalistaapartirdozero.Usamosumlaçowhilequefaznossoprogramaentraremloopeserrodado3

vezes.

Alémdeacessarumvalorespecíficoutilizandooíndice,podemosacessarmúltiplosvaloresatravésdofatiamento.Tambémutilizamoscolchetesparaofatiamento.Suponhaquequeremosacessarosdoisprimeiroselementosdeumalista:

>>>lista=[2,3,5,7,11]>>>lista[0:2][2,3]

Podemosteromesmocomportamentofazendo:

>>>lista[:2][2,3]

Sequeremostodososvaloresexcluindoosdoisprimeiros,fazemos:

5.2SEQUÊNCIAS 53

Page 61: Python e Orientação a Objetos

>>>lista[2:][5,7,11]

Ouutilizamosíndicesnegativos:

>>>lista[-3:][5,7,11]

Tambémpodemosfatiarumalistademodoapegarelementosemumintervaloespecífico:

>>>lista[2:4][5,7]

As listas também possuem funcionalidades prontas, e podemos manipulá-las através de funçõesembutidas. As listas podem ser utilizadas em conjunto com uma função chamada append(), que

adicionaumdadonalista:

>>>lista=[]>>>lista.append('zero')>>>lista.append('um')>>>lista['zero','um']

Afunçãoappend()sóconsegueinserirumelementoporvez.Sequisermosinserirmaiselementos,

podemossomaroumultiplicarlistas,ouentãoutilizarafunçãoextend():

>>>lista=['zero','um']>>>lista.extend(['dois','três',])>>>lista+=['quatro','cinco']>>>lista+['seis']['zero','um','dois','três','quatro','cinco','seis']>>>lista*2['zero','um','dois','três','quatro','cinco','seis','zero','um','dois','três','quatro','cinco','seis']

Isso é possível pois listas são sequênciasmutáveis, ou seja, conseguimos adicionar, remover emodificarseuselementos.Paraimprimiroconteúdodeumalista,podemosutilizarocomandofor:

forvalorinlista:...print(valor)...zeroumdoistrêsquatrocinco

Tuplas

Uma tupla é uma lista imutável, ou seja, uma tupla é uma sequência que não pode ser alteradadepoisdecriada.Umatuplaédefinidadeformaparecidacomumalistacomadiferençadodelimitador.Enquantolistasutilizamcolchetescomodelimitadores,astuplasusamparênteses:

>>>dias=('domingo','segunda','terça','quarta','quinta','sexta','sabado')>>>type(dias)

54 5.2SEQUÊNCIAS

Page 62: Python e Orientação a Objetos

<class'tuple'>

Podemosomitirosparênteseseinseriroselementosseparadosporvírgula:

>>>dias='domingo','segunda','terça','quarta','quinta','sexta','sabado'>>>type(dias)>>><class'tuple'>

Noteque,naverdade,éavírgulaquefazumatupla,nãoosparênteses.Osparêntesessãoopcionais,excetonocasodatuplavazia,ouquandosãonecessáriosparaevitarambiguidadesintática.

Assimcomoaslistas,tambémpodemosusarumafunçãoparacriarumatuplapassandoumtipoquepodeseriterávelcomoumastringouumalista.Essafunçãoéatuple():

>>>texto='python'>>>tuple(texto)('p','y','t','h','o','n')>>>lista=[1,2,3,4]>>>tuple(lista)(1,2,3,4)

Asregrasparaosíndicessãoasmesmasdaslistas,excetoparaelementostambémimutáveis.Comosãoimutáveis,umavezcriadasnãopodemosadicionarnemremoverelementosdeumatupla.Ométodoappend()dalistanãoexistenatupla:

>>>dias.append('sabado2')Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>AttributeError:'tuple'objecthasnoattribute'append'>>>>>>dias[0]'domingo'>>>>>>dias[0]='dom'File"<stdin>",line1,in<module>TypeError:'tuple'objectdoesnotsupportitemassignment

Nãoépossívelatribuirvaloresaositensindividuaisdeumatupla,noentanto,épossívelcriartuplasquecontenhamobjetosmutáveis,comolistas.

>>>lista=[3,4]>>>tupla=(1,2,lista)>>>tupla(1,2,[3,4])>>>lista=[4,4]>>>tupla(1,2,[4,4])>>>tupla[2]=[3,4]Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:'tuple'objectdoesnotsupportitemassignment

Astuplassãoimutáveisegeralmentecontêmumasequênciaheterogêneadeelementos.Jáaslistassão mutáveis, e seus elementos geralmente são homogêneos, sendo acessados pela iteração da lista,emboranãosejaumaregra.

5.2SEQUÊNCIAS 55

Page 63: Python e Orientação a Objetos

Quandoénecessárioarmazenarumacoleçãodedadosquenãopossaseralterada,prefirausartuplasalistas.Outravantageméquetuplaspodemserusadascomochavesdedicionários,quediscutiremosnasseçõesadiante.

TuplassãofrequentementeusadasemprogramasPython.Umusobastantecomumsãoemfunçõesquerecebemmúltiplosvalores.Astuplasimplementamtodasasoperaçõesdesequênciacomuns.

Range

Orangeéumtipodesequênciaimutáveldenúmeros,sendocomumenteusadoparaloopingdeumnúmeroespecíficodevezesemumcomandoforjáquerepresentamumintervalo.Ocomandorangegeraumvalorcontendonúmerosinteirossequenciais,obedecendoasintaxe:

range(inicio,fim)

Onúmerofinalizador,ofim,nãoéincluídonasequência.Vejamosumexemplo:

>>>sequencia=range(1,3)>>>print(sequencia)range(1,3)

Orangenão imprimeoselementosdasequência,eleapenasarmazenaseu inícioeseufinal.Paraimprimirseuselementosprecisamosdeumlaçofor:

>>>forvalorinrange(1,3):...print(valor)...12

Observequeelenãoincluiosegundoparâmetrodafunçãorangenasequência.Outracaracterísticadestecomandoéadepodercontrolaropassodasequênciaadicionandoumterceiroparâmetro,istoé,avariaçãoentreumnúmeroeoseusucessor:

>>>forvalorinrange(1,10,2):...print(valor)...13579

Os intervalos implementam todas as operações de seqüência comuns, exceto concatenação erepetição(devidoaofatodequeobjetosdeintervalosópodemrepresentarsequênciasqueseguemumpadrãoestritoearepetiçãoeaconcatenaçãogeralmenteviolamessepadrão).

OPythontambémincluiumtipodedadosparaconjuntos.Umconjunto,diferentedeumasequência,

5.3CONJUNTOS

56 5.3CONJUNTOS

Page 64: Python e Orientação a Objetos

éumacoleçãonãoordenadaequenãoadmiteelementosduplicados.

Chavesouafunçãoset()podemserusadosparacriarconjuntos.

>>>frutas={'laranja','banana','uva','pera','laranja','uva','abacate'}>>>frutas>>>{'uva','abacate','pera','banana','laranja'}>>>type(frutas)<class'set'>

Usos básicos incluem testes de associação e eliminação de entradas duplicadas. Os objetos deconjunto também suportam operações matemáticas como união, interseção, diferença e diferençasimétrica.Podemostransformarumtextoemumconjuntocomafrunçãoset()etestarosoperações:

>>>a=set('abacate')>>>b=set('abacaxi')>>>a{'a','e','c','t','b'}>>>b{'a','x','i','c','b'}>>>a-b#diferença{'e','t'}>>>a|b#união{'c','b','i','t','x','e','a'}>>>a&b#interseção{'a','c','b'}>>>a^b#diferençasimétrica{'i','t','x','e'}

Notequepara criarumconjuntovaziovocê temqueusarset(), não{}; o segundo cria um

dicionáriovazio,umaestruturadedadosquediscutiremosnapróximaseção.

>>>a=set()>>>aset()>>>b={}>>>b{}>>>type(a)<class'set'>>>>type(b)<class'dict'>

Vimosquelist,tuple,rangeestrsãosequênciasordenadasdeobjetos,esetssãocoleções

deelementosnãoordenados.DicionárioéoutraestruturadedadosemPythoneseuselementos,sendoestruturadas de forma não ordenada assim como os conjuntos. Ainda assim, essa não é a principaldiferença com as listas. Os dicionários são estruturas poderosas e muito utilizadas, já que podemosacessar seus elementos através de chaves e não por sua posição. Em outras linguagens, este tipo éconhecidocomo"matrizesassociativas".

Qualquer chave de um dicionário é associada (ou mapeada) a um valor. Os valores podem ser

5.4DICIONÁRIOS

5.4DICIONÁRIOS 57

Page 65: Python e Orientação a Objetos

qualquertipodedadodoPython.Portanto,osdicionáriossãoparesdechave-valornãoordenados.

Osdicionáriospertencemaotipodemapeamentointegradoenãosequenciaiscomoaslistas,tuplasestrings.Vamosvercomoissofuncionanocódigoecriarumdicionáriocomdadosdeumapessoa:

>>>pessoa={'nome':'João','idade':25,'cidade':'SãoPaulo'}>>>pessoa'nome':'João','idade':25,'cidade':'SãoPaulo'

Osdicionáriossãodelimitadosporchaves({})esuaschaves('nome','idade'e'cidade')poraspas.Jáosvalorespodemserdequalquertipo.Noexemploacima,temosduasstringseumint.

Oqueseráqueacontecesetentarmosacessarseuprimeiroelemento?

>>>pessoa[0]Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>KeyError:0

Nãoépossívelacessarumelementodeumdicionárioporumíndicecomonalista.Devemosacessá-losporsuachave:

>>>pessoa['nome']'João'>>>pessoa['idade']25

Seprecisarmosadicionaralgumelemento,comoporexemplo,opaís,bastafazermos:

>>>pessoa1['país']='Brasil'>>>pessoa1{'nome':'João','idade':25,'cidade':'SãoPaulo','país':'Brasil'}

Comosempreacessamosseuselementosatravésdechaves,odicionáriopossuiummétodochamadokeys()quedevolveoconjuntodesuaschaves:

>>>pessoa1.keys()dict_keys(['nome','idade','cidade','pais'])

Assimcomoummétodochamadovalues()queretornaseusvalores:

>>>pessoa1.values()dict_values(['João',25,'SãoPaulo','Brasil'])

Notequeas chavesdeumdicionárionãopodemser iguaisparanãocausar conflito.Alémdisso,somentetiposdedadosimutáveispodemserusadoscomochaves,ouseja,nenhumalistaoudicionáriopodeserusado.Casoissoaconteça,recebemosumerro:

>>>dic={[1,2,3]:'valor'}Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:unhashabletype:'list'

Jáastuplas,comochaves,sãopermitidas:

58 5.4DICIONÁRIOS

Page 66: Python e Orientação a Objetos

>>>dic={(1,2,3):'valor'}>>>dic{(1,2,3):'valor'}

Tambémpodemoscriardicionáriosutilizandoafunçãodict():

>>>a=dict(um=1,dois=2,três=3)>>>a{'três':3,'dois':2,'um':1}

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Vamostentarresolveralgunsdesafios.Dadaalista=[12,-2,4,8,29,45,78,36,-17,2,12,8,3,3,-52]façaumprogramaque:

a)imprimaomaiorelemento

b)imprimaomenorelemento

c)imprimaosnúmerospares

d)imprimaonúmerodeocorrênciasdoprimeiroelementodalista

e)imprimaamédiadoselementos

f)imprimaasomadoselementosdevalornegativo

1. Primeiramente,vamosgerarumnovoarquivoparaestecódigo,chamadolista.py.Crieoarquivo

noPycharmdentrodomesmoprojeto'jogos'criadonocapítuloanterior.

2. Vamoscomeçargerandoumloopparapercorreralista.Utilizamosumforjuntocomumrange

parapercorrercadaíndicedanossalista:

lista=[12,-2,4,8,29,45,78,36,-17,2,12,12,3,3,-52]

forindexinrange(0,len(lista)):

Seuslivrosdetecnologiaparecemdoséculopassado?

5.5EXERCÍCIOS:ESTRUTURADEDADOS

5.5EXERCÍCIOS:ESTRUTURADEDADOS 59

Page 67: Python e Orientação a Objetos

3. Agora,vamos resolvero itema.Definaumavariável forado seu for chamadamaiorValor e a

igualeaoprimeiroelementonalista.Dentrodoseufor,percorraoselementosdentrodeumif

parasubstituirovalorencontradocasosejamaiordoqueomesmo:

lista=[12,-2,4,8,29,45,78,36,-17,2,12,12,3,3,-52]

maiorValor=lista[0]

forindexinrange(0,len(lista)):#Maiorvalorif(maiorValor<lista[index]):maiorValor=lista[index]

print(maiorValor)

4. Pararesolveroitemb,bastaseguiramesmaideiadoexemploanterior.Crieumoutroifabaixo

doquevocêcriounopassoanterior,apenasmudandoooperadordacondição:

menorValor=lista[0]

forindexinrange(0,len(lista)):#...seucódigoaqui#Menorvalorif(menorValor>lista[index]):menorValor=lista[index]

print(menorValor)

5. Pararesolveroitemc,bastadefinirumalista,ecasoovaloratualdalistacommódulo2retorne0,

eleéadicionadonamesma:

listaPares=[]

forindexinrange(0,len(lista)):#...seucódigoaqui#Numerosparesif(lista[index]%2==0):listaPares.append(lista[index])print(listaPares)

6. Pararesolveroitemd,éprecisoverificarseoitematualdalistaaserpercorridacoincidecomo

elementoemseuprimeiroíndice:

ocorrenciasItem1=0

forindexinrange(0,len(lista)):#...seucódigoaqui#Numerodeocorrenciasif(lista[index]==lista[0]):ocorrenciasItem1=ocorrenciasItem1+1

print(ocorrenciasItem1)

7. Aresoluçãodoitemerequeraimplementaçãodentroeforadofor.Dentrodofor,somecada

um dos elementos em uma variável única. Após o loop, divida o valor obtido pelo total de

elementosnasualista:

60 5.5EXERCÍCIOS:ESTRUTURADEDADOS

Page 68: Python e Orientação a Objetos

mediaElementos=0

forindexinrange(0,len(lista)):#...seucódigoaqui#MediadeelementosmediaElementos=+mediaElementos+lista[index]mediaElementos=mediaElementos/len(lista)

print(mediaElementos)

8. Por fim, para resolver o itemf, faça a somade todosos númerosnegativos somando todosos

valoresquesãomenoresque0:

somaNegativos=0

forindexinrange(0,len(lista)):#...seucódigoaqui#Somadosnúmerosnegativosif(lista[index]<0):somaNegativos=somaNegativos+lista[index]

print(somaNegativos)

9. Tenteimprimirtodasestascondiçõesnoseuloopevejaoresultado.Oseuresultadodeveráestar

similara:

```python

lista=[12,-2,4,8,29,45,78,36,-17,2,12,12,3,3,-52]

#declarandonossasvariáveismaiorValor=lista[0]menorValor=lista[0]listaPares=[]ocorrenciasItem1=0mediaElementos=0somaNegativos=0

#iniciandoonossoloop:forindexinrange(0,len(lista)):

#Maiorvalorif(maiorValor<lista[index]):maiorValor=lista[index]

#Menorvalorif(menorValor>lista[index]):menorValor=lista[index]

#Numerosparesif(lista[index]%2==0):listaPares.append(lista[index])

#Numerodeocorrênciasif(lista[index]==lista[0]):ocorrenciasItem1=ocorrenciasItem1+1

#Somadosnúmerosnegativosif(lista[index]<0):somaNegativos=somaNegativos+lista[index]

5.5EXERCÍCIOS:ESTRUTURADEDADOS 61

Page 69: Python e Orientação a Objetos

#MediadosomatóriodoselementosmediaElementos=+mediaElementos+lista[index]

mediaElementos=mediaElementos/len(lista)

print("Maiorvalor:"+str(maiorValor))print("Menorvalor:"+str(menorValor))print("Listadeelementospares:"+str(listaPares))print("Númerodeocorrênciasdoprimeiroitem:"+str(ocorrenciasItem1))print("Médiadoselementos:"+str(mediaElementos))print("Somatóriodosvaloresnegativos:"+str(somaNegativos))```

62 5.5EXERCÍCIOS:ESTRUTURADEDADOS

Page 70: Python e Orientação a Objetos

CAPÍTULO6

Objetivos:

entenderoconceitodefunçãosabereusaralgumasfunçõesembutidasdalinguagemcriarumafunção

Oconceitodefunçãoéumdosmaisimportantesnamatemática.Emcomputação,umafunçãoéumasequênciadeinstruçõesquecomputaumoumaisresultadosquechamamosdeparâmetros.Nocapítuloanterior,utilizamosalgumasfunçõesjáprontasdoPython,comooprint(),input(),format()etype().

Tambémpodemoscriarnossasprópriasfunções.Porexemplo,quandoqueremoscalculararazãodoespaçopelotempo,podemosdefinirumafunçãorecebendoestesparâmetros:

f(espaco,tempo)=espaco/tempo

Essarazãodoespaçopelotempoéoquechamamosdevelocidademédianafísica.Podemosentãodarestenomeanossafunção:

velocidade(espaco,tempo)=espaco/tempo

Se um carro percorreu uma distância de 100 metros em 20 segundos, podemos calcular suavelocidademédia:

velocidade(100,20)=100/20=5m/s

OPythonpermitedefinirmosfunçõescomoessadavelocidademédia.Asintaxeémuitoparecidacomadamatemática.ParadefinirmosumafunçãonoPython,utilizamosocomandodef:

defvelocidade(espaco,tempo):pass

Logoapósodefvemonomedafunção,eentreparêntesesvêmosseusparâmetros.Umafunção

tambémtemumescopoeumblocodeinstruçõesondecolocamososcálculos,ondeestesdevemseguiraidentaçãopadrãodoPython(4espaçosadireita).

Comonossafunçãoaindanãofaznada,utilizamosapalavrachavepassparadizeraointerpretadorquedefiniremososcálculosdepois.Apalavrapassnãoéusadaapenasemfunções,podemosusarem

FUNÇÕES

6.1OQUEÉUMAFUNÇÃO?

6FUNÇÕES 63

Page 71: Python e Orientação a Objetos

qualquerblocodecomandoscomonasinstruçõesif,whileefor,porexemplo.

Vamossubstituirapalavrapasspeloscálculosquenossafunçãodeveexecutar:

defvelocidade(espaco,tempo):v=espaco/tempoprint('velocidade:{}m/s'.format(v))

Nossa função faz o cálculo da velocidade média e utiliza a função print() do Python para

imprimi-lonatela.Vamostestarnossafunção:

velocidade(100,20)

#velocidade:5m/s

Demaneirageral,umafunçãoéumestruturaparaagruparumconjuntodeinstruçõesquepodemserreutilizadas.Agoraqualquerpartedonossoprogramapodechamarafunçãovelocidadequandoprecisarcalcularavelocidademédiadeumveículo,porexemplo.Epodemoschamá-lamaisdeumavez,oquesignificaquenãoprecisamosescreveromesmocódigonovamente.

Funções são conhecidas por diversos nomes em linguagens de programação como subrotinas,rotinas,procedimentos,métodosesubprogramas.

Podemosterfunçõessemparâmetros.Porexemplo,podemosterumafunçãoquediz'oi'natela:

defdiz_oi():print("oi")diz_oi()

#oi

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Umconjuntodeparâmetrosconsisteemumalistacomnenhumoumaiselementosquepodemserobrigatórios ou opcionais. Para um parâmetro ser opcional, omesmo é atribuído a um valor padrão

Agoraéamelhorhoradeaprenderalgonovo

6.2PARÂMETROSDEFUNÇÃO

64 6.2PARÂMETROSDEFUNÇÃO

Page 72: Python e Orientação a Objetos

(default)-omaiscomuméutilizarNone.Porexemplo:

defdados(nome,idade=None):print('nome:{}'.format(nome))if(idadeisnotNone):print('idade:{}'.format(idade))else:print('idade:nãoinformada')

O código da função acima recebe uma idade como parâmetro e faz uma verificação com umainstruçãoif:seaidadefordiferentedeNoneelavaiimprimiraidade,casocontráriovaiimprimiridadenãoinformada.Vamostestarpassandoosdoisparâmetrosedepoisapenasonome:

dados('joão',20)

#nome:joão#idade:20

Agorapassandoapenasonome:

dados('joão')

#nome:joão#idade:nãoinformada

Eoqueacontecesepassarmosapenasaidade?

dados(20)

#nome:20#idade:nãoinformada

VejaqueoPythonobedeceaordemdosparâmetros.Nossaintençãoerapassaronúmero20comoidade,masointerpretadorvaientenderqueestamospassandoonomeporquenãoavisamosissoà

ele.Casoqueiramospassarapenasaidade,devemosespecificaroparâmetro:

dados(idade=20)File"<stdin>",line1,in<module>TypeError:dados()missing1requiredpositionalargument:'nome'

Ointerpretadoriráacusarumerro,jáquenãopassamosoatributoobrigatórionome.

Eseaoinvésdeapenasmostraroresultado,quisermosutilizaravelocidademédiaparafazeroutrocálculo,comocalcularaaceleração?Damaneiracomoestá,nossafunçãovelocidade()nãoconsegue

utilizarseuresultadofinalparacálculos.

Exemplo:aceleracao=velocidade(parametros)/tempo

Paraconseguirmosestecomportamento,precisamosquenossafunçãoretorneovalorcalculadoporela.EmPythoneemoutraslinguagensdeprogramação,istoéalcançadoatravésdocomandoreturn:

6.3FUNÇÃOCOMRETORNO

6.3FUNÇÃOCOMRETORNO 65

Page 73: Python e Orientação a Objetos

defvelocidade(espaco,tempo):v=espaco/temporeturnv

Testando:

print(velocidade(100,20))#5.0

Ouainda,podemosatribuí-laaumavariável:

resultado=velocidade(100,20)print(resultado)#5.0

Econseguimosutilizá-lanocálculodaaceleração:

aceleracao=velocidade(100,20)/20print(aceleracao)#0.25

Umafunçãopodecontermaisdeumcomandoreturn.Porexemplo,nossafunçãodados() queimprimeonomeeaidade,podeagoraretornarumastring.Repareque,nestecaso,temosduassituaçõespossíveis: a que a idade é passada por parâmetro e a que ela não é passada. Aqui, teremos doiscomandosreturn:

defdados(nome,idade=None):if(idadeisnotNone):return('nome:{}\nidade:{}'.format(nome,idade))else:return('nome:{}\nidade:nãoinformada'.format(nome))

Apesardafunçãopossuirdoiscomandosreturn,elatemapenasumretorno--vairetornarumouooutro.Quandoafunçãoencontraumcomandoreturn,elanãoexecutamaisnadaquevierdepoisdeledentrodeseuescopo.

Apesardeumafunçãoexecutarapenasumretorno,emPythonpodemosretornarmaisdeumvalor.Vamos fazer uma função calculadora que vai retornar os resultados de operações básicas entre doisnúmeros:adição(+)esubtração(-),nestaordem.

Pararetornarmúltiplosvalores,retornamososresultadosseparadosporvírgula:

defcalculadora(x,y):returnx+y,x-y

print(calculadora(1,2))

#(3,-1)

Qualseráotipoderetornodestafunção?Vamosperguntaraointerpretadoratravésdafunçãotype:

print(type(calculadora(1,2)))

6.4RETORNANDOMÚLTIPLOSVALORES

66 6.4RETORNANDOMÚLTIPLOSVALORES

Page 74: Python e Orientação a Objetos

#<class'tuple'>

Damaneiraquedefinimosoretorno,afunçãodevolveumatupla.Nestecasoespecífico,poderíamosretornarumdicionárioeusarumlaçoforparaimprimirosresultados:

defcalculadora(x,y):return{'soma':x+y,'subtração':x-y}

resultados=calculadora(1,2)forkeyinresultados:print('{}:{}'.format(key,resultados[key]))

#soma:3#subtração:-1

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

1. Definaumafunçãochamadavelocidade_media()emumscriptchamadofuncoes.py que recebe

doisparâmetros:adistânciapercorrida(emmetros)eotempo(emsegundos)gasto.

defvelocidade_media(distancia,tempo):pass

2. Agoravamosinseriras instruções,ouseja,oqueafunçãodevefazer.Vamosinseriroscomandosparacalcularavelocidademédiaeguardaroresultadoemumavariávelvelocidade:

defvelocidade_media(distancia,tempo):velocidade=distancia/tempo

3. Vamosfazerafunçãoimprimirovalordavelocidademédiacalculada:

defvelocidade_media(distancia,tempo):velocidade=distancia/tempoprint(velocidade)

4. Teste o seu código chamando a funçãopara os valores abaixo e compare os resultados com seus

EditoraCasadoCódigocomlivrosdeumaformadiferente

6.5EXERCÍCIOS:FUNÇÕES

6.5EXERCÍCIOS:FUNÇÕES 67

Page 75: Python e Orientação a Objetos

colegas:

distância:100,tempo=20distância:150,tempo=22distância:200,tempo=30distância:50,tempo=3

5. Modifiqueafunçãovelocidade_media()demodoqueelaretorneoresultadocalculado.

6. Definaumafunçãosoma()querecebedoisnúmeroscomoparâmetrosecalculaasomaentreeles.

7. Definaumafunçãosubtracao()querecebedoisnúmeroscomoparâmetrosecalculaadiferença

entreeles.

8. Agora façauma funçãocalculadora() que recebe dois números como parâmetros e retorna o

resultadodasomaedasubtraçãoentreeles.

9. Modifiqueafunçãocalculadora()doexercícioanteriorefaçaelaretornartambémoresultadoda

multiplicaçãoedivisãodosparâmetros.

10. Chameafunçãocalculadora()comalgunsvalores.

11. (opcional)Definauma funçãodivisao() que recebedois números comoparâmetros, calcula e

retornaoresultadodadivisãodoprimeiropelosegundo.Modifiqueafunçãovelocidade_media()

utilizandoafunçãodivisao()paracalcularavelocidade.Testeoseucódigochamandoafunção

velocidade_media()comovaloresabaixo:a.distância:100,tempo=20b.distância:-20,tempo

=10c.distância:150,tempo=0

Podemos passar um número arbitrário de parâmetros em uma função. Utilizamos as chamadasvariáveismágicasdoPython:*argse**kwargs.Muitosprogramadorestemdificuldadesementenderessasvariáveis.Vamosentenderoqueelassão.

Nãoénecessárioutilizarexatamenteestesnomes:*argse**kwargs.Apenasoasterisco(*),ou

doisdeles(**), serãonecessários.Podemosoptar, por exemplo, emescrever*var e**vars.Mas

*argse**kwargséumaconvençãoentreacomunidadequetambémseguiremos.

Primeiro, aprenderemos a usar o*args. É usado, assim como o**kwargs, em definições de

funções.*argse**kwargspermitempassarumnúmerovariáveldeargumentosdeumafunção.O

que a variável significa é que o programador ainda não sabe de antemão quantos argumentos serãopassadosparasuafunção,apenasquesãomuitos.Então,nestecasousamosapalavrachave*args.

6.6NÚMEROARBITRÁRIODEPARÂMETROS(*ARGS)

68 6.6NÚMEROARBITRÁRIODEPARÂMETROS(*ARGS)

Page 76: Python e Orientação a Objetos

Vejaumexemplo:

defteste(arg,*args):print('primeiroargumentonormal:{}'.format(arg))forarginargs:print('outroargumento:{}'.format(arg))

teste('python','é','muito','legal')

Quevaigerarasaída:

primeiroargumentonormal:pythonoutroargumento:éoutroargumento:muitooutroargumento:legal

O parâmetro arg é como qualquer outro parâmetro de função, já o *args recebe múltiplos

parâmetros.Viucomoéfácil?Tambémpoderíamosconseguiromesmoresultadopassandoumlist

outupledeargumentos,acrescidodoasterisco:

lista=["é","muito","legal"]teste('python',*lista)

Ouainda:

tupla=("é","muito","legal")teste('python',*tupla)

O*argsentãoéutilizadoquandonãosabemosdeantemãoquantosargumentosqueremospassar

paraumafunção.Oasterisco(*)executaumempacotamentodosdadosparafacilitarapassagemde

parâmetros,eafunçãoquerecebeestetipodeparâmetroécapazdefazerodesempacotamento.

O**kwargspermitequepassemosotamanhovariáveldapalavra-chavedosargumentosparauma

função.Vocêdeveusaro**kwargssequisermanipularargumentosnomeadosemumafunção.Veja

umexemplo:

defminha_funcao(**kwargs):forkey,valueinkwargs.items():print('{0}={1}'.format(key,value))

>>>minha_funcao(nome='caelum')nome=caelum

Tambémpodemospassarumdicionárioacrescidodedoisasteriscos,jáquesetratadechaveevalor:

dicionario={'nome':'joao','idade':25}minha_funcao(**dicionario)idade=25nome=joao

Adiferençaéqueo*argsesperaumatupladeargumentosposicionais,enquantoo**kwargsum

6.7NÚMEROARBITRÁRIODECHAVES(**KWARGS)

6.7NÚMEROARBITRÁRIODECHAVES(**KWARGS) 69

Page 77: Python e Orientação a Objetos

dicionáriocomargumentosnomeados.

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

1. Crieumarquivocomumafunçãochamadateste_args_kwargs()querecebetrêsargumentose

imprimecadaumdeles:

defteste_args_kwargs(arg1,arg2,arg3):print("arg1:",arg1)print("arg2:",arg2)print("arg3:",arg3)

2. Agoravamoschamarafunçãoutilizandoo*args:

args=('um',2,3)teste_args_kwargs(*args)

Quegeraasaída:

arg1:umarg2:2arg3:3

3. Testeamesmafunçãousandoo**kwargs.Paraisso,crieumdicionáriocomtrêsargumentos:

kwargs={'arg3':3,'arg2':'dois','arg1':'um'}teste_args_kwargs(**kwargs)

Quedevegerarasaída:

arg1:umarg2:doisarg3:3

4. (Opcional)Tentechamaramesmafunção,masadicionandoumquartoargumentonavariávelargsekwargsdosexercíciosanteriores.Oqueaconteceseafunçãorecebemaisdoque3argumentos?

JáconheceoscursosonlineAlura?

6.8EXERCÍCIO-*ARGSE**KWARGS

70 6.8EXERCÍCIO-*ARGSE**KWARGS

Page 78: Python e Orientação a Objetos

5. Dequemaneiravocêresolveriaoproblemadoexercícioanterior?

Discutacomoinstrutoreseuscolegasquandousar*argse**kwargs.

1. Vamos começar definindo uma função jogar que conterá toda lógica do jogo da forca. Abra oarquivoforca.pyecoloqueocódigodojogoemumafunçãojogar():

defjogar():#códigodojogoaqui

Emseguida,chameafunçãojogar()logoabaixodadefiniçãodafunçãonoarquivoforca.py:

>>>jogar()"*********************************""***BemvindoaojogodaForca!***""*********************************"['_','_','_','_','_','_']Qualletra?

Agoranossojogofuncionacomoesperado.

2. Façaomesmocomojogodaadivinhaçãoeexecuteojogo.

3. Agoravamoscriarummenuparaqueousuáriopossaescolherumjogo(adivinhaçãoouforca).Crieoarquivomenu.pydemodoqueanossaestruturadearquivosfiqueassim:

|_home|_jogos|_advinhacao.py|_forca.py|_menu.py

Importeosarquivosdecadajogodentrodomenu.py:

importadivinhacaoimportforca

Obs:Certifique-sede removerachamadada função jogar() logoabaixodadefiniçãodasmesmasnosarquivosforca.pyeadivinhação.py,poiscasoestaspermaneçamnosarquivos,aschamadasaosjogosserãorealizadasautomaticamenteaogerarosimportsnoarquivomenu.py!

Peçaparaousuárioescolherumadasopções:

#Importamódulos(códigoomitido)

print('*********************************')print('**********MENUDEJOGOS**********')print('*********************************')print("1.Adivinhação")print("2.Forca")escolha=int(input("Qualjogoquerjogar?Digiteonúmero:"))

6.9EXERCÍCIO-FUNÇÃOJOGAR()

6.9EXERCÍCIO-FUNÇÃOJOGAR() 71

Page 79: Python e Orientação a Objetos

ifescolha==1:#Jogaradivinhaçãoelifescolha==2:#Jogarforca

Chameafunçãojogar()domódulodojogoescolhido:

#Importamóduloserecebeaescolhadousuário(códigoomitido)

ifescolha==1:adivinhacao.jogar()elifescolha==2:forca.jogar()

Agora toda vez que o usuário quiser jogar umde nossos jogos, ele poderá escolher pormeio domenucriado.

Aoimportaroarquivoforca.py,estamosimportandoummódulodenossoprograma,quenadamaisédoqueumarquivo.Vamosverificarotipodeforca:

print(type(forca))<class'module'>

Vejaqueotipoéummódulo.Antesdecontinuarmoscomnossojogo,vamosaprenderumpoucomais sobre arquivos e módulos. Vamos melhorar ainda mais nosso jogo da Forca e utilizar o queaprendemosdefunçõesparaorganizarnossocódigo.

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

6.10MÓDULOSEOCOMANDOIMPORT

VocêpodetambémfazerocursodatadessaapostilanaCaelum

72 6.10MÓDULOSEOCOMANDOIMPORT

Page 80: Python e Orientação a Objetos

CAPÍTULO7

Umafuncionalidadequeaindanosatrapalhanojogodaforcaéapalavrasecreta,queatualmenteestáfixa.Sequeremosqueapalavrasejadiferente,devemosmodificá-lanocódigo.

A nossa ideia é ler palavras de um arquivo de texto, e dentre elas escolhemos uma palavraaleatoriamente,queseráapalavrasecretadojogo.

Paraabrirumarquivo,oPythonpossuiafunçãoopen().Elarecebedoisparâmetros:oprimeiroé

o nome do arquivo a ser aberto, e o segundo parâmetro é omodo que queremos trabalhar com essearquivo-sequeremoslerouescrever.Omodoépassadoatravésdeumastring:"w"(abreviaçãoparawrite)paraescritae"r"(abreviaçãopararead)paraleitura.

Nojogo,faremosaleituradeumarquivo.Antes,vamostestarcomofuncionaaescritanoterminaldoPython3:

>>>arquivo=open('palavras.txt','w')

Omodoéopcionaleomodopadrãoéo"r"deleitura(reading)queveremosmaisadiante.

Oarquivocriadosechama'palavras.txt'eestánomododeescrita.Éimportantesaberqueomodode escrita sobrescreve o arquivo, se omesmo existir. Se a intenção é apenas adicionar conteúdo aoarquivo,utilizamosomodo"a"(abreviaçãoparaappend).

Agoraquetemosoarquivo,vamosaprenderaescreveralgumconteúdonele.Bastachamarapartirdoarquivoafunçãowrite(),passandoparaelaoquesequerescrevernoarquivo:

>>>arquivo.write('banana')6>>>arquivo.write('melancia')8

Oretornodessafunçãoéonúmerodecaracteresdecadatextoadicionadonoarquivo.

ARQUIVOS

7.1ESCRITADEUMARQUIVO

7ARQUIVOS 73

Page 81: Python e Orientação a Objetos

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Quando estamos trabalhando com arquivos, devemos nos preocupar em fechá-lo. Para fechá-lousamosafunçãoclose():

>>>arquivo.close()

Apósisso,podemosverificaroconteúdodoarquivo.ReparequeelefoicriadonamesmapastaemqueocomandoparaabriroconsoledoPython3foiexecutado.Sevocêtentarfecharumarquivoquejáestá fechado, não vai surtir efeito algum, nem mesmo um erro. Abra o arquivo na pasta criada everifiqueseuconteúdo:

bananamelancia

Aspalavrasforamescritasemumamesmalinha.Mascomoescreverumanovalinha?

Aprimeiracoisaquedevemosfazeréabriroarquivonovamente,dessavezutilizandoomodo'a',deappend:

arquivo=open('palavras.txt','a')

Vamosescrevernovamentenoarquivo,masdessavezcomapreocupaçãodecriarumanovalinhaapóscadaconteúdoescrito.Pararepresentarumanovalinhaemcódigo,adicionamoso \nao finaldoquequeremosescrever:

>>>arquivo.write('morango\n')8>>>arquivo.write('manga\n')

Aofecharoarquivoeverificarnovamenteoseuconteúdo,vemos:

Seuslivrosdetecnologiaparecemdoséculopassado?

7.2FECHANDOUMARQUIVO

7.3ESCREVENDOPALAVRASEMNOVASLINHAS

74 7.2FECHANDOUMARQUIVO

Page 82: Python e Orientação a Objetos

bananamelanciamorangomanga

Apalavramorangoaindaficounamesmalinha,mascomoespecificamosnasuaadiçãoqueapósapalavradeveráterumaquebradelinha,apalavramangafoiadicionadaabaixo,emumanovalinha.

Porfim,vamosmoverestearquivoparanossoprojetoeajeitarsuaspalavrasquebrandoaslinhas.

1. Vamosnavegaraténossapasta jogosdentrodehome.Lembrandoquenossaestruturadearquivosestáassim:

|_home|_jogos|_advinhacao.py|_forca.py|_menu.py

2. Crieumoutroarquivo,chamadoarquivo.pyeinsiraoseguintecódigo:

arquivo=open("palavras.txt","w")

Rodeoarquivoevejaoresultado.Oseudiretóriodeveráestarsimilara:

|_home|_jogos|_advinhacao.py|_forca.py|_arquivo.py|_menu.py|_palavras.txt

3. Vamoscomeçaraescrevernonossoarquivoutilizandoafunçãowrite()aspalavrasqueusaremos

nonossojogodaforca:

arquivo.write('banana\n')

arquivo.write('melancia\n')

arquivo.write('morango\n')

arquivo.write('manga\n')

Note que ao final de cada palavra temos que acrescentar o "\n" para a quebra de linha, que vaifacilitarnahoradaleitura.

4. Éumaboapráticafecharoarquivodepoisdeutilizá-lo,assimoutrosprogramasouprocessospodemteracessoaoarquivoeelenãoficapresoapenasaonossoprogramaPython.

arquivo.close()

7.4EXERCÍCIOS

7.4EXERCÍCIOS 75

Page 83: Python e Orientação a Objetos

PARASABERMAIS

Alémdor,weaexisteomodificadorbqueéutilizadoquandosedeseja trabalharnomodobinário.Paraabrirumaimagemnomodoleitura,devemosusar:

```pythonimagem=open('foto.jpg','rb')```

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

AindanoterminaldoPython3,veremosofuncionamentodaleituradeumarquivo.Comoagoraoarquivopalavras.txtestánapastadoprojetojogos,devemosexecutarocomandoqueabreoterminal

doPython3napastadoprojeto.

$cdjogos$python3

Vamosentãoabriroarquivonomododeleitura,bastapassaronomedoarquivoealetra"r"paraafunçãoopen(),comojávistoanteriormente.

arquivo=open('palavras.txt','r')

Diferentedomodo"w",abrirumarquivoquenãoexistenomodo"r"nãovaicriarumarquivo.Se"palavras.txt"nãoexistir,oPythonvailançaroerroFileNotFoundError:

arquivo.open('frutas.txt','r')

Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>FileNotFoundError:[Errno2]Nosuchfileordirectory:'frutas.txt'

Agoraéamelhorhoradeaprenderalgonovo

7.5LENDOUMARQUIVO

76 7.5LENDOUMARQUIVO

Page 84: Python e Orientação a Objetos

Comooarquivofrutas.txtnãoexistenapastajogos,oPyhtonnãoconsegueencontrareacusaquenãoexistenenhumarquivooudiretóriocomestenomenapastaraiz.

Comoabrimosoarquivonomododeleitura,afunçãowrite()nãoésuportada:

arquivo.write("oi")

Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>io.UnsupportedOperation:notwritable

Paraleroarquivointeiro,utilizamosafunçãoread():

arquivo.read()

'banana\nmelancia\nmorango\nmanga\n'

Masaoexecutarafunçãonovamente,seráretornadoumastringvazia:

arquivo.read()''

Issoaconteceporqueoarquivoécomoumfluxodelinhas,quecomeçanoiníciodoarquivocomosefosseumcursor.Elevaidescendoelendooarquivo.Apóslertudo,eleficaposicionadonofinaldoarquivo.Quandochamamosafunçãoread()novamente,nãohámaisconteúdopoisele todo já foi

lido.

Portanto,paraleroarquivonovamente,devemosfechá-loeabrí-looutravez:

arquivo.close()arquivo=open('palavras.txt','r')arquivo.read()

Nãoqueremoslertodooconteúdodoarquivomaslerlinhaporlinha.Comojáfoivisto,umarquivoéumfluxodelinhas,ouseja,umasequênciadelinhas.Sendoumasequência,podemosutilizarumlaçoforparalercadalinhadoarquivo:

arquivo=open('palavras.txt','r')forlinhainarquivo:print(linha)...banana

melancia

morango

manga

Reparequeexisteumalinhavaziaentrecadafruta.Issoaconteceporqueestamosutilizandoafunçãoprint() que também acrescenta, por padrão, um \n . Agora vamos utilizar outra função, a

7.6LENDOLINHAPORLINHADOARQUIVO

7.6LENDOLINHAPORLINHADOARQUIVO 77

Page 85: Python e Orientação a Objetos

readline(),quelêapenasumalinhadoarquivo:

arquivo=open('palavras.txt','r')linha=arquivo.readline()print(linha)

'banana\n'

Háum\n ao final de cada linha, de cada palavra,mas queremos somente a palavra. Para tirar

espaçosembranconoinícioenofimdastring,bastautilizarafunçãostrip(),quetambémremove

caracteresespeciais,comoo\n-paramaisinformaçõesconsulteadocumentaçãodestrings.Sabendo

dissotudo,jápodemosimplementarafuncionalidadedeleituradearquivononossojogo:

arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:linha=linha.strip()palavras.append(linha)

arquivo.close()

Agorajátemostodasaspalavrasnalista,mascomoselecionarumadelasaleatoriamente?

Sabemosquecadaelementodalistapossuiumaposiçãoevimosnotreinamentoanteriorcomogerarumnúmeroaleatório.Abibliotecaquesabegerarumnúmeroaleatórioéarandom.Vamostestá-lanoterminaldoPython3,primeiroimportando-a:

>>>importrandom

Para gerar o número aleatório utilizamos a biblioteca e chamamos a funçãorandrange(), que

recebe o intervalo de valores que o número aleatório deve estar. Então vamos passar o valor 0(equivalente à primeira posição da nossa lista) e 4 (lembrando que o número é exclusivo, ou seja, onúmeroaleatórioseráentre0e3,equivalenteàúltimaposiçãodanossalista):

>>>importrandom>>>random.randrange(0,4)0>>>random.randrange(0,4)1>>>random.randrange(0,4)3>>>random.randrange(0,4)1>>>random.randrange(0,4)3

Sabendodisso,vamosimplementaressecódigononossojogo.

7.7GERANDOUMNÚMEROALEATÓRIO

78 7.7GERANDOUMNÚMEROALEATÓRIO

Page 86: Python e Orientação a Objetos

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

1.Vamos começar abrindo o arquivo "palavras.txt" no nosso script forca.py que criamos no

exercícioanterior.Éimportantejáacrescentarocomandoparafechá-loparanãoesquecernofuturo:

defjogar():print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

arquivo=open('palavras.txt','r')arquivo.close()

#restantedocódigo

1. Agoravamoscriarumalistachamadapalavrasefazerumlaçoforparaacessarcadalinhapara

guardarnalista:

arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:palavras.append(linha)

arquivo.close()

2. Comoprecisamosremovero\naofinaldalinha,usaremosafunçãostrip()emcadalinha:

defjogar():

arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:linha-linha.strip()palavras.append(linha)

arquivo.close()

EditoraCasadoCódigocomlivrosdeumaformadiferente

7.8EXERCÍCIOS-LEITURADEARQUIVOS

7.8EXERCÍCIOS-LEITURADEARQUIVOS 79

Page 87: Python e Orientação a Objetos

3. Devemos importar a biblioteca random para gerar um número que vai de 0 até a quantidade depalavrasdanossalista.Usaremosafunçãolen()parasaberotamanhodalistaearandrange()

paragerarumnúmerorandômicodeumintervaloespecífico:

importrandom

print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:linha=linha.strip()palavras.append(linha)

arquivo.close()

numero=random.randrange(0,len(palavras))

4. Agoraquetemosonúmeroaleatório,vamosutilizá-locomoíndiceparaacessaralistaeatribuiressapalavraàvariávelpalavra_secreta:

importrandom

print("*********************************")print("***BemvindoaojogodaForca!***")print("*********************************")

arquivo=open("palavras.txt","r")palavras=[]

forlinhainarquivo:linha=linha.strip()palavras.append(linha)

arquivo.close()

numero=random.randrange(0,len(palavras))

palavra_secreta=palavras[numero].upper()letras_acertadas=['_'forletrainpalavra_secreta]

5. Porfim,temosquedeixarnossavariávelletras_acertadasdinâmica,comnúmerodeletrasde

acordocomnossapalavra_secreta.Vamosutilizarumfordentrodalistaparagerarum'_'para

cadaletradeacordocomotamanhodapalavra_secreta:

letras_acertadas=['_'forletrainpalavra_secreta]

6. Como já garantimos que a palavra_secreta está toda em letras maiúsculas com o código

palavras[numero].upper(),modificaremosochuteparaoprimeiroifcontinuarfuncionando

chute=input('Qualaletra?')chute=chute.strip().upper()

Podemosexecutarojogoenotarqueapalavraéselecionadaaleatoriamente!

80 7.8EXERCÍCIOS-LEITURADEARQUIVOS

Page 88: Python e Orientação a Objetos

Mas agora a nossa função cresceu bastante, com várias funcionalidades e responsabilidades. Nopróximo capítulo organizaremosmelhor o nosso código, separando-o em funções e deixando-omaisfácildeentender.

Já falamosda importânciade fecharoarquivo, certo?Vejaocódigoabaixoque justamenteusaafunçãoclose():

arquivo=open('palavras.txt','r')palavras=logo.read()arquivo.close()

Imaginequealgumproblemaaconteçanahorada leituraquandoafunçãoread()échamada.Seráqueoarquivoéfechadoquandooerroocorre?

Seforalgumerrograve,oprogramapodepararaexecuçãosemterfechadooarquivoeistoseriabastanteruim.Paraevitaressetipodesituação,existenoPythonumasintaxeespecialparaaberturadearquivo:

withopen('palavras.txt')asarquivo:forlinhainarquivo:print(linha)

Repareocomandowithusaafunçãoopen(),masnãoafunçãoclose().Issonãoserámais

necessário, já que o comando with vai se encarregar de fechar o arquivo para nós, mesmo que

aconteçaalgumerronocódigodentrodeseuescopo.Muitomelhor,não?

Noscapítulosanteriorescriamosdoisjogos,avançamosnojogodaForcaeimplementamosleituradepalavrasemumarquivo.Agoravamosutilizaroqueaprendemosdefunçõesparaencapsularnossocódigo e deixá-lomais organizado.Vamos começar, comos conhecimentos que temos até aqui, paraestruturarnossojogodaForca.

A função jogar() possui um código muito complexo, com muitas funcionalidades e

responsabilidades.

Entre as funcionalidadesqueo códigopossui, está a apresentaçãodo jogo, a leiturado arquivo einicializaçãodapalavrasecreta,entreoutras.Vamosentãosepararas responsabilidadesdocódigoemfunções,melhorandoasualegibilidadeeorganização.

Vamoscomeçarcomamensagemdeapresentaçãodonossojogoeexportarocódigoparaafunçãoimprime_mensagem_abertura(). Não podemos nos esquecer de chamar essa função no início da

funçãojogar():

7.9PARASABERMAIS-COMANDOWITH

7.10MELHORANDONOSSOCÓDIGO

7.9PARASABERMAIS-COMANDOWITH 81

Page 89: Python e Orientação a Objetos

importrandom

defjogar():imprime_mensagem_abertura()

#códigoomitido

defimprime_mensagem_abertura():print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

Aquinãoimportaolocaldafunção,elapodeserdeclaradaantesoudepoisdafunçãojogar().

Oquefizemosfoirefatorarnossocódigo.Refatoraçãoéoprocessodemodificarumprogramaparamelhoraraestruturainternadocódigo,semalterarseucomportamentoexterno.VejaqueseexecutarmosnossojogodaForca,tudofuncionacomoantes:

*********************************)***BemvindoaojogodaForca!************************************['_','_','_','_','_','_']Qualletra?

Nopróximoexercício,vamosrefatorarasdemaispartesdonossocódigo.

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

1. Crieafunçãoimprime_mensagem_aberturaquevaiisolaramensagemdeaberturadojogo:

importrandom

defjogar():imprime_mensagem_abertura()

#códigoomitido

defimprime_mensagem_abertura():

JáconheceoscursosonlineAlura?

7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA

82 7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA

Page 90: Python e Orientação a Objetos

print('*********************************')print('***BemvindoaojogodaForca!***')print('*********************************')

2. Agora, vamos separar o código que realiza a leitura do arquivo e inicializa a palavra secreta nafunçãocarrega_palavra_secreta():

defcarrega_palavra_secreta():arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:linha=linha.strip()palavras.append(linha)

arquivo.close()

numero=random.randrange(0,len(palavras))palavra_secreta=palavras[numero].upper()

Sóqueafunçãojogar()iráreclamarqueapalavra_secretanãoexiste.Oquequeremoséque,ao

executarafunçãocarrega_palavra_secreta(),queelaretorneapalavrasecretaparanós,assim

poderemosguardá-laemumavariável:

importrandom

defjogar():

imprime_mensagem_abertura()

palavra_secreta=carrega_palavra_secreta()

letras_acertadas=["_"forletrainpalavra_secreta]

#restantedocódigoomitido

Só que como faremos a função carrega_palavra_secreta() retornar um valor, no caso a

palavra_secreta ? A palavra_secreta já existe, mas só dentro da função

carrega_palavra_secreta().Paraqueelasejaretornada,utilizamosapalavra-chavereturn:

defcarrega_palavra_secreta():arquivo=open('palavras.txt','r')palavras=[]

forlinhainarquivo:linha=linha.strip()palavras.append(linha)

arquivo.close()

numero=random.randrange(0,len(palavras))palavra_secreta=palavras[numero].upper()

returnpalavra_secreta

3. Agora, vamos criar uma função que inicializa a lista de letras acertadas com o caractere '_'.Criaremosafunçãoinicializa_letras_acertadas():

7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA 83

Page 91: Python e Orientação a Objetos

importrandom

defjogar():

imprime_mensagem_abertura()

palavra_secreta=carrega_palavra_secreta()

letras_acertadas=inicializa_letras_acertadas()

#códigoomitido

definicializa_letras_acertadas():return['_'forletrainpalavra_secreta]

4. Masafunçãoinicializa_letras_acertadas()precisateracessoàpalavra_secreta,poiselanão

existedentroda função, jáqueumafunçãodefineumescopo,easvariáveisdeclaradasdentrodeuma função só estão disponíveis dentro dela. Então, ao chamar a funçãoinicializa_letras_acertadas(),vamospassarpalavra_secretaparaelaporparâmetro:

importrandom

defjogar():

imprime_mensagem_abertura()

palavra_secreta=carrega_palavra_secreta()

letras_acertadas=inicializa_letras_acertadas(palavra_secreta)

#restantedocódigoomitido

definicializa_letras_acertadas(palavra):return["_"forletrainpalavra]

5. Vamoscontinuarrefatorandonossocódigo.Criaremosafunçãopede_chute(),queficarácomo

códigoquepedeochutedousuário,removeosespaçosantesedepois,eocolocaemcaixaalta.Nãopodemosnosesquecerderetornarochute:

defjogar():#códigoomitido

while(notacertouandnotenforcou):chute=pede_chute()#códigoomitido

#códigoomitido

defpede_chute():chute=input('Qualletra?')chute=chute.strip().upper()returnchute

6. Aindatemosocódigoquecolocaochutenaposiçãocorreta,dentrodalista.Vamoscolocá-lodentrodafunçãomarca_chute_correto():

while(notacertouandnotenforcou):

84 7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA

Page 92: Python e Orientação a Objetos

chute=pede_chute()

if(chuteinpalavra_secreta):marca_chute_correto()else:erros+=1

enforcou=erros==6acertou='_'notinletras_acertadasprint(letras_acertadas)

#códigoomitido

defmarca_chute_correto():posicao=0forletrainpalavra_secreta:if(chute==letra):letras_acertadas[posicao]=letraposicao+=1

Mas a função marca_chute_correto() precisa ter acesso a três valores: palavra_secreta,

chuteeletras_acertadas.Assim,vamospassaressesvaloresporparâmetro:

if(chuteinpalavra_secreta):marca_chute_correto(chute,letras_acertadas,palavra_secreta)

Emodificamosnossafunçãoparareceberessesparâmetros:

defmarca_chute_correto(chute,letras_acertadas,palavra_secreta):posicao=0forletrainpalavra_secreta:if(chute==letra):letras_acertadas[posicao]=letraposicao+=1

1. Por fim, vamos remover a mensagem de fim de jogo e exportar os códigos que imprimem asmensagensdevencedoreperdedordojogo:

if(acertou):imprime_mensagem_vencedor()else:imprime_mensagem_perdedor()

Ecriarasfunções:

defimprime_mensagem_vencedor():print('Vocêganhou!')

defimprime_mensagem_perdedor():print('Vocêperdeu!')

Agoraonossocódigoestámuitomaisorganizadoelegível.Aochamartodasasfunçõesdentrodafunçãojogar(),nossocódigoficaráassim:

defjogar():imprime_mensagem_abertura()palavra_secreta=carrega_palavra_secreta()

letras_acertadas=inicializa_letras_acertadas(palavra_secreta)

7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA 85

Page 93: Python e Orientação a Objetos

print(letras_acertadas)

enforcou=Falseacertou=Falseerros=0

while(notenforcouandnotacertou):

chute=pede_chute()

if(chuteinpalavra_secreta):marca_chute_correto(chute,letras_acertadas,palavra_secreta)else:erros+=1

enforcou=erros==6acertou="_"notinletras_acertadas

print(letras_acertadas)

if(acertou):imprime_mensagem_vencedor()else:imprime_mensagem_perdedor()

Por fim, podemos executar o nosso código, para verificar que o mesmo continua funcionandonormalmente.

************************************BemvindoaojogodaForca!************************************['_','_','_','_','_','_']Qualletra?

2. (opcional)Comamelhororganizaçãodonossocódigo,vamosmelhoraraexibição,aapresentaçãodaforca,deixandoojogomaisamigável.Vamoscomeçarcomamensagemdeperdedor,alterandoafunçãoimprime_mensagem_perdedor.Elaficaráassim:

defimprime_mensagem_perdedor(palavra_secreta):print('Puxa,vocêfoienforcado!')print('Apalavraera{}'.format(palavra_secreta))print("_______________")print("/\")print("/\")print("//\/\")print("\|XXXXXXXX|/")print("|XXXXXXXX|/")print("|XXXXXX|")print("||")print("\__XXX__/")print("|\XXX/|")print("||||")print("|IIIIIII|")print("|IIIIII|")print("\__/")print("\__/")print("\_______/")

Precisamospassarapalavra_secretaparafunçãoimprime_mensagem_perdedor():

86 7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA

Page 94: Python e Orientação a Objetos

if(acertou):imprime_mensagem_vencedor()else:imprime_mensagem_perdedor(palavra_secreta)

Emodificamosocódigodeimprime_mensagem_vencedor()para:

defimprime_mensagem_vencedor():print('Parabéns,vocêganhou!')print("___________")print("'._==_==_=_.'")print(".-\\:/-.")print("|(|:.|)|")print("'-|:.|-'")print("\\::./")print("'::..'")print(")(")print("_.''._")print("'-------'")

Vá até a pasta do curso e copie o código destas funções que estão em um arquivo chamadofuncoes_forca.py.

1. (opcional)Porfim,crieafunçãodesenha_forca(),queirádesenharumapartedaforca,baseado

noserrosdousuário.Comoelaprecisaacessaroserros,passe-oporparâmetroparaafunção:

defdesenha_forca(erros):pass

Ecopieoconteúdodocódigodafunçãodesenha_forca()doarquivofuncoesforca.pynapasta

docursoparasuafunção.

2. Parafinalizar,chameafunçãodesenha_forcaquandoojogadorerrareaumenteolimitedeerros

para7.

if(chuteinpalavra_secreta):marca_chute_correto(chute,letras_acertadas,palavra_secreta)else:erros+=1desenha_forca(erros)

enforcou=erros==7acertou="_"notinletras_acertadas

3. Tentefazeromesmocomojogodaadivinhação,refatorepartesdocódigoeisoleemfunções.Alémdisso,usesuacriatividadeparacustomizarmensagensparaousuáriodoseujogo.

Nesteexercício,praticamosbastantedoqueaprendemosnocapítulodefunçãoefinalizamosojogodaforca.

Vocêpodeestarseperguntandoporqueencapsulamosumasimpleslinhadecódigoemumafunção.Fizemosissosomenteparadeixarclarooqueestamosfazendo,melhorandoalegibilidadedocódigo.Masprecisamostomarcuidadocomacriaçãodefunções,poiscriarfunçõesdesnecessariamentepode

7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA 87

Page 95: Python e Orientação a Objetos

aumentaracomplexidadedocódigo.

88 7.11EXERCÍCIO-REFATORANDOOJOGODAFORCA

Page 96: Python e Orientação a Objetos

CAPÍTULO8

Considereumprogramaparaumbancofinanceiro.Éfácilperceberqueumaentidadeimportanteparaonosso sistema será uma conta.Primeiramente, suponhaquevocê temumaconta nessebanco comasseguintes características: titular, número, saldo e limite. Vamos começar inicializando essascaracterísticas:

>>>numero='123-4'>>>titular="João">>>saldo=120.0>>>limite=1000.0

Eseanecessidadederepresentarmaisdeumacontasurgir?Vamoscriarmaisuma:

>>>numero1='123-4'>>>titular1="João">>>saldo1=120.0>>>limite1=1000.0>>>>>>numero2='123-5'>>>titular2="José">>>saldo2=200.0>>>limite2=1000.0

Nossobancopodeviracrescere termilharesdecontase,damaneiraqueestáoprograma, seriamuitotrabalhosodarmanutenção.

Ecomoutilizarosdadosdeumadeterminadacontaemoutroarquivo?Podemosutilizaraestruturadodicionárioqueaprendemosanteriormenteeagruparessascaracterísticas.Issovaiajudaraacessarosdadosdeumacontaespecífica:

conta={"numero":'123-4',"titular":"João","saldo":120.0,"limite":1000.0}

Agoraépossívelacessarosdadosdeumacontapelonomedachave:

>>>conta['numero']'123-4'>>>conta['titular']'João'

Paracriarumasegundaconta,crieoutrodicionário:

conta2={"numero":'123-5',"titular":"José","saldo":200.0,"limite":1000.0}

Avançamos em agrupar os dados de uma conta,mas ainda precisamos repetir seguidamente essalinhadecódigoacadacontacriada.Podemosisolaressecódigoemumafunçãoresponsávelporcriar

ORIENTAÇÃOAOBJETOS

8ORIENTAÇÃOAOBJETOS 89

Page 97: Python e Orientação a Objetos

umaconta:

defcria_conta():conta={"numero":'123-4',"titular":"João","saldo":120.0,"limite":1000.0}returnconta

Mas ainda não é o ideal, já que queremos criar contas com outros valores e tornar a criaçãodinâmica.Vamosentãoreceberessevalorescomoparâmetrosdafunção,eporfimretornamosaconta:

defcria_conta(numero,titular,saldo,limite):conta={"numero":numero,"titular":titular,"saldo":saldo,"limite":limite}returnconta

Destamaneiraépossívelcriarváriascontascomdadosdiferentes:

>>>conta1=cria_conta('123-4','João',120.0,1000.0)>>>conta2=cria_conta('123-5','José',200.0,1000.0)

Paraacessaronúmerodecadaumadelas,fazemos:

>>>conta1['numero']'123-4'>>>conta2['numero']'123-5'

Já descrevemos as características de uma conta e nosso próximo passo será descrever suasfuncionalidades.Oquefazemoscomumaconta?Ora,podemosdepositarumvaloremumaconta,porexemplo.Vamoscriarumafunçãopararepresentarestafuncionalidade.Alémdovaloraserdepositado,precisamossaberqualcontareceberáestevalor:

defdeposita(conta,valor):conta['saldo']=conta['saldo']+valor

Veja que estamos repetindo conta['saldo'] duas vezes nessa linha de código. O Python permiteescreveramesmacoisadeumamaneiramaiseleganteutilizandoo'+=':

defdeposita(conta,valor):conta['saldo']+=valor

Podemosfazeralgosemelhantecomafunçãosaca():

defsaca(conta,valor):conta['saldo']-=valor

Antesdetestaressasfuncionalidades,crieoutraquemostraoextratodaconta:

defextrato(conta):print("numero:{}\nsaldo:{}".format(conta['numero'],conta['saldo']))

Oextratoimprimeasinformaçõesdacontautilizandoafunçãoprint().Agorapodemostestaro

código(supondoqueomesmoestejaemumarquivochamadoteste.py):

8.1FUNCIONALIDADES

90 8.1FUNCIONALIDADES

Page 98: Python e Orientação a Objetos

conta=cria_conta('123-4','João',120.0,1000.0)deposita(conta,15.0)extrato(conta)

#numero:'123-4'#saldo:135.0

saca(conta,20.0)extrato(conta)

#numero:'123-4'#saldo115.0

Ótimo!Nossocódigofuncionoucomooesperado.Aplicamosalgumasfunçõescomodeposita()

esaca(),eaofinalpudemoschecarosaldofinalcomafunçãoextrato().

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

1. Crieumapastachamadaooemsuaworkspaceecrieumarquivochamadoteste_conta.py

2. Crieafunçãochamadacria_conta(),querecebecomoargumentonumero,titular,saldoe

limite:

defcria_conta(numero,titular,saldo,limite):

3. Dentrodecria_conta(), crieumavariáveldo tipodicionáriochamadaconta comas chaves

recebendoosvaloresdosparâmetros(numero,titular,saldoelimite),eaofinal,retornea

conta:

defcria_conta(numero,titular,saldo,limite):conta={"numero":numero,"titular":titular,"saldo":saldo,"limite":limite}returnconta

4. Crieuma funçãochamadadeposita() nomesmoarquivoteste_conta.py que recebe como

argumentoumacontaeumvalor.Dentrodafunção,adicioneovaloraosaldodaconta:

VocêpodetambémfazerocursodatadessaapostilanaCaelum

8.2EXERCÍCIO:CRIANDOUMACONTA

8.2EXERCÍCIO:CRIANDOUMACONTA 91

Page 99: Python e Orientação a Objetos

defdeposita(conta,valor):conta['saldo']+=valor

5. Crieoutrafunçãochamadasaca()querecebecomoargumentoumacontaeumvalor.Dentro

dafunção,subtraiaovalordosaldodaconta:

defsaca(conta,valor):conta['saldo']-=valor

6. E por fim, crie uma função chamada extrato(), que recebe como argumento uma conta e

imprimeonumeroeosaldo:

defextrato(conta):print("numero:{}\nsaldo:{}".format(conta['numero'],conta['saldo']))

1. Navegueatéapastaoo,digiteoscomandosnoarquivoteste_conta.pyetesteasfuncionalidades:

conta=cria_conta('123-7','João',500.0,1000.0)deposita(conta,50.0)extrato(conta)

#numero:'123-7'#saldo:550.0

saca(conta,20.0)extrato(conta)

#numero:'123-7'#saldo530.0

2. (Opcional)Acrescenteumadocumentaçãoparaoseumóduloteste_conta.pyeutilizeafunção

help()paratestá-la.

Nesteexercíciocriamosumacontaejuntamossuascaracterísticas(número,titular,limite,saldo)efuncionalidades (sacar,depositar, tirar extrato)nummesmoarquivo.Masoque fizemosatéagora foibaseadonoconhecimentoproceduralquetínhamosdoPython3.

Pormaisquetenhamosagrupadoosdadosdeumaconta,essaligaçãoéfrágilnomundoproceduralesemostralimitada.Precisamospensarsobreoqueescrevemosparanãoerrar.Oparadigmaorientadoaobjetosvemparasanaressaeoutrasfragilidadesdoparadigmaproceduralqueveremosaseguir.

Ninguémdeveriateracessoaosaldodiretamente.Alémdisso,nadanosobrigaavalidaressevalorepodemos esquecer disso cada vez que utilizá-lo. Nosso programa deveria obrigar o uso das funçõessaca()edeposita()paraalterarosaldoenãopermitiralterarovalordiretamente:

conta3['saldo']=100000000.0

ouentão:

8.3CLASSESEOBJETOS

92 8.3CLASSESEOBJETOS

Page 100: Python e Orientação a Objetos

conta3['saldo']=-3000.0

Devemosmanipularosdadosatravésdasfuncionalidadessaca() edeposita() e proteger os

dadosdaconta.Pensandonomundoreal,ninguémpodemodificarosaldodesuacontaquandoquiser,anãoserquandovamosfazerumsaqueouumdepósito.Amesmacoisadeveaconteceraqui.

Para isso, vamos primeiro entender o que é classe e objeto , conceitos importantes do

paradigmaorientadoaobjetosedepoisveremoscomoissofuncionanaprática.

Quandopreparamosumbolo,geralmenteseguimosumareceitaquedefineosingredienteseomodode preparação.A nossa conta é um objeto concreto assim como o bolo que também precisa de umareceitapré-definida.Ea"receita"nomundoOOrecebeonomedeclasse.Ouseja,antesdecriarmosumobjeto,definiremosumaclasse.

Outraanalogiaquepodemosfazeréentreoprojetodeumacasa(aplantadacasa)eacasaemsi.Oprojeto é aclasse e a casa, construída a partir desta planta, é oobjeto.O projeto da conta, isto é, adefiniçãodaconta,éaclasse.Oquepodemosconstruir(instanciar)apartirdessaclasse,ascontasdeverdade,damosonomedeobjetos.

Pode parecer óbvio,mas a dificuldade inicial do paradigma da orientação a objetos é justamentesaber distinguir o que é classe e o que é objeto. É comumo iniciante utilizar, obviamente de formaerrada,essasduaspalavrascomosinônimos.

OpróximopassoserácriarnossaclasseContadentrodeumnovoarquivoPython,quereceberáo

nomedeconta.py.CriarumaclasseemPythonéextremamentesimplesemtermosdesintaxe.Vamoscomeçar criando uma classe vazia. Depois criaremos uma instância, um objeto dessa classe, eutilizaremosafunçãotype()paraanalisaroresultado:

classConta:pass

#Emoutroarquivo:fromcontaimportConta

conta=Conta()print(type(conta))#<class'__main__.Conta'>

VemospoisomóduloondeseencontraaclasseConta énomóduloprincipal,ouseja,nanossa

__main__.Agora,temosumaclasseConta.

ComoPython é uma linguagem dinâmica, podemosmodificar esse objetoconta em tempo de

execução.Porexemplo,podemosacrescentaratributosaele:

conta.titular="João"print(conta.titular)#'João'

conta.saldo=120.0

8.3CLASSESEOBJETOS 93

Page 101: Python e Orientação a Objetos

print(conta.saldo)#120.0

Mas o problema do código é que ainda não garantimos que toda instância deConta tenha um

atributotitular ousaldo. Portanto, queremosuma formapadronizadada conta demaneira que

possamoscriarobjetoscomdeterminadasconfiguraçõesiniciais.

Em linguagens orientadas a objetos, existe uma maneira padronizada de criar atributos em umobjeto.Geralmentefazemosissoatravésdeumafunçãoconstrutora-algoparecidocomnossafunçãocria_conta()doexercícioanterior.

EmPython,algunsnomesdemétodosestãoreservadosparaousodapróprialinguagem.Umdessesmétodos é o __init__() que vai inicializar o objeto. Seu primeiro parâmetro, assim como todo

métododeinstância,éaprópriainstância.Porconvenção,chamamosesteargumentodeself.Vejamosumexemplo:

classConta:def__init__(self,numero,titular,saldo,limite):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

Agora, quando uma classe é criada, todos os seus atributos serão inicializados pelo método__init__().Apesardemuitosprogramadoreschamaremestemétododeconstrutor,elenãocriaum

objeto conta. Existe outro método, o __new__() que é chamado antes do __init_() pelo

interpretadordoPython.Ométodo__new__()érealmenteoconstrutoreéquemrealmentecriauma

instância deConta.Ométodo __init__() é responsável por inicializar o objeto, tanto é que já

recebeaprópriainstância(self)criadapeloconstrutorcomoargumento.Dessamaneira,garantimosquetodainstânciadeumaContatenhaosatributosquedefinimos.

Agora,seexecutarmosalinhadecódigoabaixo,vaiacusarumerro:

conta=Conta()Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:__init__()missing4requiredpositionalarguments:'numero','titular','saldo',and'limite

Oerro acusa a falta de4 argumentosnahorade criar umaConta.A classeConta agoranos

obrigaapassar4atributos(numero,titular,saldoelimite)paracriarumaconta:

conta=Conta('123-4','João',120.0,1000.0)

Veja que em nenhummomento chamamos ométodo__init__(). Quem está fazendo isso por

debaixodospanoséopróprioPythonquandoexecutaconta=Conta().Comovimos,elechamao

8.4CONSTRUTOR

94 8.4CONSTRUTOR

Page 102: Python e Orientação a Objetos

método__new__()quedevolveuminstânciadoobjetoeemseguidachamaométodo__init__()

todavezquecriamosumaconta.Podemosveristofuncionandoimprimindoumamensagemdentrodométodo__init__():

def__init__(self,titular,numero,saldo,limite):print("inicializandoumaconta")self.titular=titularself.numero=numeroself.saldo=saldoself.limite=limite

etestarnovamente:

conta=Conta('123-4','João',120.0,1000.0)inicializandoumaconta

Ao criar uma Conta, estamos pedindo para o Python criar uma nova instância de Conta na

memória,ouseja,oPythonalocarámemóriasuficienteparaguardar todasas informaçõesdaConta

dentro da memória do programa. O __new__(), portanto, devolve uma referência, uma seta queapontaparaoobjetoemmemóriaeéguardadanavariávelconta.

Paramanipularmos nosso objeto conta e acessarmos seus atributos, utilizamos o operador "."

(ponto):

print(conta.titular)#'João'print(conta.saldo)#120.0

Comooselféareferênciadoobjeto,elechamaself.titulareself.saldodaclasseConta.

Agora, além de funcionar como esperado, nosso código não permite criar uma conta sem osatributosquedefinimosanteriormente.Discutacomseuscolegaseinstrutorasvantagensdaorientaçãoaobjetosatéaqui.

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Seuslivrosdetecnologiaparecemdoséculopassado?

8.4CONSTRUTOR 95

Page 103: Python e Orientação a Objetos

Como vimos, além dos atributos, nossa conta deve possuir funcionalidades. No arquivoteste_conta.pycriamosasfunçõessaca(),deposita()eextrato().Noparadigmaorientado a

objetos,asfuncionalidadesdeumobjetosãochamadosdemétodos-dopontodevistadocódigo,sãoasfunçõesdentrodeumaclasse.

Vamoscriarométododeposita()naclasseConta.Aqui,assimcomoométodo__init__(),o

métododeposita()devereceberainstânciadoobjeto(self)alémdovaloraserdepositado:

classConta:

#método__init__()omitido

defdeposita(self,valor):self.saldo+=valor

Issoaconteceporqueométodoprecisasaberqualobjetocontaeledevemanipular,qualcontavaidepositarumdeterminadovalor-epodemostermuitascontascriadasnonossosistema.

Utilizamosooperador'.'(ponto)atravésdoobjetocontaparachamarométododeposita:

>>>conta.deposita(20.0)

Aoleressecódigo,ointerpretadorassociaoobjetocontaaoargumentoselfdométodo-note

quenãoprecisamospassaracontacomoargumento,issoéfeitopordebaixodospanospeloPython.

Faremosomesmoparaosmétodossaca()eextrato():

classConta:

#outrosmétodosomitidos

defsaca(self,valor):self.saldo-=valor

defextrato(self):print("numero:{}\nsaldo:{}".format(self.numero,self.saldo))

Agoravamostestarnossosmétodos:

conta=Conta('123-4','João',120.0,1000.0)conta.deposita(20.0)conta.extrato()#numero:'123-4'#saldo:140.0conta.saca(15)conta.extrato()#numero:'123-4'#saldo:125.0

O saldo inicial era de 120 reais.Depositamos 20 reais, sacamos 15 reais e tiramos o extrato queresultouem125reais.

8.5MÉTODOS

96 8.5MÉTODOS

Page 104: Python e Orientação a Objetos

Porfim,ocódigodenossaContavaificarassim:

classConta:

def__init__(self,numero,titular,saldo,limite):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

defdeposita(self,valor):self.saldo+=valor

defsaca(self,valor):self.saldo-=valor

defextrato(self):print("numero:{}\nsaldo:{}".format(self.numero,self.saldo))

EmoutraslinguagenscomoC++eJava,ummétodosempretemquedefiniroqueretorna,nemquedefinaquenãohá retorno.Comovimosnocapítulosobre funções,noPython issonãoénecessário -maspodemosretornaralgonométodosaca(),porexemplo,indicandoseaoperaçãofoibemsucedida

ounão.Nestecaso,podemosretornarumvalorbooleano:

defsaca(self,valor):if(self.saldo<valor):returnFalseelse:self.saldo-=valorreturnTrue

Veja que a declaração do método não mudou, mas agora ele nos retorna algo (um boolean). Apalavrachavereturnindicaqueométodovaiterminarali,retornandotalinformação.

Exemplodeuso:

minha_conta.saldo=1000consegui=minha_conta.saca(2000)if(consegui):print(“conseguisacar”)else:print(“nãoconseguisacar”)

#'nãoconseguisacar'

Ouentão,podemoseliminaravariáveltemporária,sedesejado:

minha_conta.saldo=1000if(minha_conta.saca(2000)):print(“conseguisacar”)else:print(“nãoconseguisacar”)

#'nãoconseguisacar'

8.6MÉTODOSCOMRETORNO

8.6MÉTODOSCOMRETORNO 97

Page 105: Python e Orientação a Objetos

Maisadiante,veremosquealgumasvezesémaisinteressantelançarumaexceção(exception)nessescasos.

OprogramapodemanternamemórianãoapenasumaConta,masmaisdeuma:

minha_conta=Conta()minha_conta.saldo=1000

meu_sonho=Conta()meu_sonho.saldo=1500000

Quando criamos uma variável para associar a umobjeto, na verdade, essa variável não guarda oobjeto,esimumamaneiradeacessá-lo,chamadadereferência(oself).

c1=Conta()

Ao fazer isso, já sabemos que o Python está chamando os métodos mágicos __new__() e

__init__()quesãoresponsáveisporconstruireiniciarumobjetodotipoConta.

Ocorretoédizerquec1serefereaumobjeto.Nãoécorretodizerquec1éumobjeto,poisc1é

umavariávelreferência.Todavia,écomumouvirfrasescomo“tenhoumobjetoc1dotipoConta”,

mas issoéapenasumaabreviaçãoparaencurtarafrase“Tenhoumareferênciac1 a umobjeto tipo

Conta”.

Vamosanalisarocódigoabaixo:

c1=Conta('123-4','João',120.0,1000.0)c2=c1print(c2.saldo)#120.0c1.deposita(100.0)print(c1.saldo)#220.0c2.deposita(30.0)print(c2.saldo)#250.0print(c1.saldo)#250.0

Oqueaconteceuaqui?Ooperador“=”copiaovalordeumavariável.Masqualéovalordavariávelc1?É o objeto?Não.Na verdade, o valor guardado é a referência (endereço) de onde o objeto se

encontranamemóriaprincipal.

8.7OBJETOSSÃOACESSADOSPORREFERÊNCIA

98 8.7OBJETOSSÃOACESSADOSPORREFERÊNCIA

Page 106: Python e Orientação a Objetos

Aofazerc2=c1,c2passaa fazer referênciaparaomesmoobjetoquec1 referencia nesse

instante.Quandoutilizamosc1ouc2nestecódigo,estamosnosreferindoaoMESMOobjeto–são

duasreferênciasqueapontamparaomesmoobjeto.

Podemosnotarissoatravésdafunçãointernaid()queretornaareferênciadeumobjeto:

print(id(c1))#140059774918104print(id(c2))#140059774918104

Internamente,c1ec2vãoguardarumnúmeroqueidentificaemqueposiçãodamemóriaaquela

Contaseencontra.Dessamaneira,aoutilizarmoso“.”(ponto)paranavegar,oPythonvaiacessara

Conta que se encontra naquela posiçãodememória, e nãoumaoutra conta.Para quemconhece, é

parecidocomumponteiro,porémvocênãopodemanipulá-lo.

Outra maneira de notar esse comportamento é que o interpretador Python chamou os métodos__new__() e __init__() apenas uma vez (na linha c1 = Conta('123-4', 'João', 120.0,

1000.0)),entãosópodehaverumobjetoContanamemória.Compará-lascomooperador“==”vai

nosretornarTrue,poisovalorqueelascarregaméomesmo:

print(id(c1)==id(c2))#Trueprint(c1==c2)#True

Podemosentãoveroutrasituação:

c1=Conta("123-4","Python",500.0,1000.0)c2=Conta("123-4","Python",500.0,1000.0)if(c1==c2):print(“contasiguais”)

Ooperador"=="comparaoconteúdodasvariáveis,masessasvariáveisnãoguardamoobjeto,esimoendereçoemqueeleseencontra.Comoemcadaumadessasvariáveisguardamosduascontascriadasdiferentemente,elasestãoemespaçosdiferentesdamemória,oquefazotestenoifvalerFalse.As

contas podem ser equivalentes no nosso critério de igualdade, porém elas não são omesmo objeto.Quandosetratadeobjetos,podeficarmaisfácilpensarqueo"=="comparaseosobjetos(referências,naverdade)sãoomesmo,enãosepossuemvaloresiguais.

8.7OBJETOSSÃOACESSADOSPORREFERÊNCIA 99

Page 107: Python e Orientação a Objetos

Para saber se dois objetos têm o mesmo conteúdo, você precisa comparar atributo por atributo.Futuramente,veremosumasoluçãomaiseleganteparaissotambém.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

E a funcionalidade que transfere dinheiro entre duas contas? Podemos ficar tentados a criar ummétodoquerecebedoisparâmetros:conta1econta2dotipoConta.Cuidado:jásabemosqueos

métodosdenossaclasseContasemprerecebemareferência,oself-portantoométodorecebeapenasumparâmetrodotipoConta,acontadestino(alémdovalor):

classConta:

#códigoomitido

deftransfere(self,destino,valor):self.saldo-=valordestino.saldo+=valor

Agoraéamelhorhoradeaprenderalgonovo

8.8MÉTODOTRANSFERE

100 8.8MÉTODOTRANSFERE

Page 108: Python e Orientação a Objetos

Para deixar o código mais robusto, poderíamos verificar se a conta possui a quantidade a sertransferidadisponível.Paraficaraindamaisinteressante,vocêpodechamarosmétodosdeposita e

sacajáexistentesparafazeressatarefa:

classConta:

#códigoomitido

deftransfere(self,destino,valor):retirou=self.saca(valor)if(retirou==False):returnFalseelse:destino.deposita(valor)returnTrue

QuandopassamosumaContacomoargumento,oqueseráqueacontecenamemória?Seráqueo

objetoéclonado?

NoPython,apassagemdeparâmetrofuncionacomoumasimplesatribuiçãocomonousodo“=”.Então,esseparâmetrovaicopiarovalordavariáveldotipoContaqueforpassadocomoargumento

para a variáveldestino.E qual é o valor de uma variável dessas? Seu valor é um endereço, uma

referência,nuncaumobjeto.Porissonãohácópiadeobjetosaqui.

Esseúltimocódigopoderiaserescritocomumasintaxemuitosucinta.Como?

TRANSFEREPARA

Perceba que o nome deste método poderia ser transfere_para() ao invés de só

transfere().Achamadadométodoficamuitomaisnatural,épossívellerafraseemportuguês

queelatemumsentido:

conta1.transfere_para(conta2,50.0):

Aleituradestecódigoseria"conta1transfereparaconta250reais".

Os atributos deuma classepodem receber umvalor padrão - assimcomoos argumentosdeumafunção. Nosso banco pode ter um valor de limite padrão para todas as contas e apenas em casosespecíficospodeatribuirumvalorlimitediferente.

Paraaplicarmosessa regradenegócio,podemosatribuirumvalorpadrãoao limite,porexemplo,1000.0reais:

8.9CONTINUANDOCOMATRIBUTOS

8.9CONTINUANDOCOMATRIBUTOS 101

Page 109: Python e Orientação a Objetos

classConta:

def__init__(self,numero,titular,saldo,limite=1000.0):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

Epodemosinicializarumaconta:

conta=Conta('123-4','joão',120.0)

Vejaqueagoranãosomosobrigadosapassarovalordolimitejáqueelepossuiumvalorpadrão

de1000.0epodemosacessá-lopelaconta:

print(conta.limite)1000.0

Quando declaramos as variáveis na classe Conta, aprendemos que podemos atribuir um valor

padrão para cada uma delas. Imagine que comecemos a aumentar nossa classe Conta e adicionar

nome, sobrenome e cpf do titular da conta. Começaríamos a ter muitos atributos e, se você pensardireito,umaContanãotemnome,nemsobrenomenemcpf,quemtemessesatributoséumcliente.Assim, podemos criar uma nova classe e fazer uma agregação - agregar um cliente a nossa conta.Portanto,nossaclasseContatemumCliente.

OatributosdeumaContatambémpodemserreferênciasparaoutrasclasses.Suponhaaseguinte

classeCliente:

classCliente:

def__init__(self,nome,sobrenome,cpf):self.nome=nomeself.sobrenome=sobrenomeself.cpf=cpf

classConta:

def__init__(self,numero,cliente,saldo,limite):self.numero=numeroself.titular=clienteself.saldo=saldoself.limite=limite

EquandocriarmosumConta,precisamospassarumClientecomotitular:

cliente=Cliente('João','Oliveira','1111111111-1')minha_conta=Conta('123-4',cliente,120.0,1000.0)

Aquiaconteceuumaatribuição,ovalordavariávelclienteécopiadoparaoatributotitular

doobjetoaoqualminha_contaserefere.Emoutraspalavras,minha_contatemumareferênciaao

mesmoClientequeclienteserefere,epodeseracessadoatravésdeminha_conta.titular.

Vocêpoderealmentenavegarsobretodaestruturadeinformação,sempreusandooponto:

102 8.9CONTINUANDOCOMATRIBUTOS

Page 110: Python e Orientação a Objetos

print(minha_conta.titular)#<__main__.Clienteobjectat0x7f83dac31dd8>

VejaqueasaídaéareferênciaaumobjetodotipoCliente,maspodemosacessarseusatributos

deumaformamaisdiretaeatémaiselegante:

print(minha_conta.titular.nome)#'João'

Pythonéumalinguagemtotalmenteorientadaaobjetos.TudoemPythonéumobjeto!Semprequeutilizamosumafunçãooumétodoquerecebeparâmetros,estamospassandoobjetoscomoargumentos.Não é diferente com nossas classes. Quando uma conta recebe um cliente como titular, ele estárecebendoumainstânciadeCliente,ouseja,umobjeto.

Omesmoacontececomnumero,saldoelimite.StringsenúmerossãoclassesnoPython.Por

estemotivo que aparece a palavra class quando pedimos para o Python nos devolver o tipo de umavariávelatravésdafunçãotype:

print(type(conta.numero))#<class'str'>print(type(conta.saldo))#<class'float'>print(type(conta.titular))#<class'__conta__.Cliente'>

Umsistemaorientadoaobjetoséumgrandeconjuntodeclassesquevaisecomunicar,delegandoresponsabilidadesparaquemformaisaptoarealizardeterminadatarefa.AclasseBancousaaclasse

Conta, queusa a classeCliente, queusa a classeEndereco, etc...Dizemosque esses objetos

colaboram,trocandomensagensentresi.Porisso,acabamostendomuitasclassesemnossosistema,eelascostumamterumtamanhorelativamentecurto.

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

8.10TUDOÉOBJETO

EditoraCasadoCódigocomlivrosdeumaformadiferente

8.10TUDOÉOBJETO 103

Page 111: Python e Orientação a Objetos

Fizemos, no ponto anterior, uma agregação. Agora nossa classe Conta tem um Cliente eassociamosestasduasclasses.Todavia,nossaclasseClienteexisteindependentedaclasseConta.

Suponha agoraquenossaConta possua um histórico, contendo a data de abertura da conta e suas

transações.Podemoscriarumaclassepararepresentarohistórico,comonoexemploabaixo:

importdatetime

classHistorico:

def__init__(self):self.data_abertura=datetime.datetime.today()self.transacoes=[]

defimprime(self):print("dataabertura:{}".format(self.data_abertura))print("transações:")fortinself.transacoes:print("-",t)

AgoraprecisamosmodificarnossaclasseContademodoqueelatenhaumHistorico.Masaqui,

diferenteda relaçãodoclientecomumaconta,aexistênciadeumhistóricodependedaexistênciadeumaConta:

classConta:

def__init__(self,numero,cliente,saldo,limite=1000.0):self.numero=numeroself.cliente=clienteself.saldo=saldoself.limite=limiteself.historico=Historico()

Epodemos,emcadamétodoparamanipularumaConta,acrescentaraoperaçãonastransaçõesde

seuHistorico:

classConta:

#códigoomitido

defdeposita(self,valor):self.saldo+=valorself.historico.transacoes.append("depósitode{}".format(valor))

defsaca(self,valor):if(self.saldo<valor):returnFalseelse:self.saldo-=valorself.historico.transacoes.append("saquede{}".format(valor))

defextrato(self):print("numero:{}\nsaldo:{}".format(self.numero,self.saldo))self.historico.transacoes.append("tirouextrato-saldode{}".format(self.saldo))

8.11COMPOSIÇÃO

104 8.11COMPOSIÇÃO

Page 112: Python e Orientação a Objetos

deftransfere_para(self,destino,valor):retirou=self.saca(valor)if(retirou==False):returnFalseelse:destino.deposita(valor)self.historico.transacoes.append("transferenciade{}paraconta{}".format(valor,destino.numero))returnTrue

Etestamos:

cliente1=Cliente('João','Oliveira','11111111111-11')cliente2=Cliente('José','Azevedo','222222222-22')conta1=Conta('123-4',cliente1,1000.0)conta2=Conta('123-5',cliente2,1000.0)conta1.deposita(100.0)conta1.saca(50.0)conta1.transfere_para(conta2,200.0)conta1.extrato

#numero:123-4#saldo:850.0

conta1.historico.imprime()

#dataabertura:2018-05-1019:44:07.406533#transações:#-depósitode100.0#-saquede50.0#-saquede200.0#-transferenciade200.0paraconta123-5#-tirouextrato-saldode850.0

conta2.historico.imprime()

#dataabertura:2018-05-1019:44:07.406553#transações:#-depósitode200.0

Quandoaexistênciadeumaclassedependedeoutraclasse,comoéa relaçãodaclasseHistóricocom a classe Conta, dizemos que a classe Historico compõe a classe Conta. Esta associação

chamamosComposição.

Mas,esedentrodanossaContanãocolocássemosself.historico=Historico(),eaoinvés

dissotentássemosacessá-lodiretamente?Fazalgumsentidofazerhistorico=Historico()?

Quandooobjetoéinicializado,elevaireceberovalordefaultquedefinimosnaclasse:

classConta:

def__init__(self,numero,cliente,saldo,limite):#iniciandooutrosparâmetrosself.historico=Historico()

Comessecódigo,todanovaContacriadajáteráumnovoHistoricoassociado,semnecessidade

deinstanciá-lologoemseguidadainstanciaçãodeumaConta.

8.11COMPOSIÇÃO 105

Page 113: Python e Orientação a Objetos

Atenção:paraquemnãoestáacostumadocomreferências,podeserbastanteconfusopensarsempreem como os objetos estão namemória para poder tirar as conclusões de o que ocorrerá ao executardeterminado código, por mais simples que ele seja. Com o tempo, você adquire a habilidade derapidamente saber o efeito de atrelar as referências, sem ter de gastar muito tempo para isso. Éimportantenessecomeçovocêestarsemprepensandonoestadodamemória.Alémdisso,lembrarquenoPython“umavariávelnuncacarregaumobjeto,esimumareferênciaparaele”facilitamuito.

Ointerpretadoradicionaalgunsatributosespeciaissomenteparaleituraaváriostiposdeobjetosdeumaclasse,sendoumdeleso__dict__.

Isso acontece porque a classe Conta possui alguns métodos, dentre eles o __init__() e o

__new__(),quesãochamadosparacriare inicializarumobjetodestaclasse, respectivamente.Caso

vocêqueirasaberquaisoutrosmétodossãoimplementadospelaclasseConta,vocêpodeusarafunção

embutidadir(),queirálistartodosmétodoseatributosqueaclassepossui.

>>>dir(Conta)['__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__','extrato','deposita','limite','numero','saca','saldo','transfere_para','titular']

Dessalista,jáconhecemoso__init__(),o__new__()eosmétodoseatributosquedefinimos

quandoconstruímosaclasseConta.Naverdade, quandousamos a funçãodir(), o interpretador

chamaoatributo__dir__dessalista.Umoutroatributobastanteútiléo__dict__,queretornaum

dicionáriocomosatributosdaclasse.

cliente=Cliente('João','Oliveira','111111111-11')conta=Conta('123-4',cliente,1000.0)print(conta.__dict__)#{'saldo':1000.0,'numero':'123-4','titular':<__main__.Clienteobjectat0x7f0b6d028f28>,'limite':1000.0}

Mas não é comum acessá-lo dessa maneira. Esses métodos iniciados e terminados com doisunderscores são chamados pelo interpretador, e são conhecidos comométodosmágicos. Existe outrafunçãoembutidadoPython,a funçãovars(),quechamaexatamenteo__dict__ de uma classe.

Obtemosomesmoresultadousandovars(conta):

vars(conta)#{'saldo':1000.0,'numero':'123-4','titular':<__main__.Clienteobjectat0x7f0b6d028f28>,'limite':1000.0}

Reparequeo__dict__eovars()retornamexatamenteumdicionáriodeatributosdeumaconta

como tínhamosmodelado no início deste capítulo. Portanto, nossas classes utilizam dicionários paraarmazenarinformaçõesdaprópriaclasse.

8.12PARASABERMAIS:OUTROSMÉTODOSDEUMACLASSE

106 8.12PARASABERMAIS:OUTROSMÉTODOSDEUMACLASSE

Page 114: Python e Orientação a Objetos

Osdemaismétodosmágicosestãodisponíveisparausoenãoutilizaremosporenquanto.Voltaremosafalardelesemumoutromomento.

1. Crieumarquivochamadoconta.pynapastaoocriadanoexercícioanterior.

2. CrieaclasseContasemnenhumatributoesalveoarquivo.

classConta:pass

3. Vá até a pasta onde se encontra o arquivo conta.py e crie um novo arquivo, chamado

conta_teste.py.DentrodestearquivoimporteaclasseContadomóduloconta.

fromcontaimportConta

4. Crieumainstância(objeto)daclasseContaeutilizea funçãotype() para verificar o tipodo

objeto:

conta=Conta()type(conta)<class'conta.Conta'>

Alémdisso,criealgunsatributosetenteacessá-los.

5. Abra novamente o arquivo conta.py e escreva o método __init__(), recebendo os atributos

anteriormentedefinidospornósquetodacontadeveter(numerotitular,saldoelimite):

classConta:

def__init__(self,numero,titular,saldo,limite):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

6. Noarquivoconta_teste.py,Tentecriarumacontasempassarqualquerargumentonoconstrutor:

conta=Conta()Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:__init__()missing4requiredpositionalarguments:'numero','titular','saldo',and'limite

Note que o interpretador acusou um erro.Ométodo__init__() exige 4 argumentos 'numero',

'titular','saldo'e'limite'.

7. Agoravamosseguiroexigidopelaclasse,pelareceitadeumaconta:

conta=Conta('123-4','João',120.0,1000.0)

8. Ointerpretadornãoacusounenhumerro.Vamosimprimironumeroetitulardaconta:

8.13EXERCÍCIO:PRIMEIRACLASSEPYTHON

8.13EXERCÍCIO:PRIMEIRACLASSEPYTHON 107

Page 115: Python e Orientação a Objetos

print(conta.numero)'123-4'print(conta.titular)'João'

9. Crieométododeposita()dentrodaclasseConta.Essemétododevereceberumareferênciado

próprioobjetoeovaloraseradicionadoaosaldodaconta.

defdeposita(self,valor):self.saldo+=valor

10. Crieométodosaca()querecebecomoargumentoumareferênciadopróprioobjetoeovaloraser

sacado.Essemétodosubtrairáovalordosaldodaconta.

defsaca(self,valor):self.saldo-=valor

11. Crie ométodoextrato(), que recebe comoargumentouma referência dopróprioobjeto.Esse

métodoimprimiráosaldodaconta:

defextrato(self):print("numero:{}\nsaldo:{}".format(self.numero,self.saldo))

12. Modifiqueométodosaca() fazendo retornarumvalorque representa seaoperação foiounão

bemsucedida.Lembrequenãoépermitidosacarumvalormaiordoqueosaldo.

defsaca(self,valor):if(self.saldo<valor):returnFalseelse:self.saldo-=valorreturnTrue

13. Crieométodotransfere_para()querecebecomoargumentoumareferênciadopróprioobjeto,

umaContadestinoeovalorasertransferido.Essemétododevesacarovalordopróprioobjetoe

depositarnacontadestino:

deftransfere_para(self,destino,valor):retirou=self.saca(valor)if(retirou==False):returnFalseelse:destino.deposita(valor)returnTrue

14. Porfim,crieduascontasnoarquivoteste-conta.pyeverifiqueosmétodoscriados.

conta.deposita(50.0)conta.extrato()conta.saca(20.0)conta.extrato()

1. (Opcional) Crie uma classe para representar um cliente do nosso banco que deve ter nome ,

sobrenomeecpf.InstancieumaContaepasseumclientecomotitulardaconta.Modifique

108 8.13EXERCÍCIO:PRIMEIRACLASSEPYTHON

Page 116: Python e Orientação a Objetos

ométodoextrato() da classeConta para imprimir, alémdonúmero e o saldo, os dadosdo

cliente.PodemoscriarumaContasemumCliente?EumClientesemumaConta?

2. (Opcional) Crie uma classe que represente uma data, com dia, mês e ano. Crie um atributodata_aberturanaclasseConta.CrieumanovacontaefaçatestesnoconsoledoPython.

3. (Desafio) Crie uma classe Historico que represente o histórico de uma Conta seguindo o

exemplodaapostila.FaçatestesnoconsoledoPythoncriandoalgumascontas,fazendooperaçõeseporúltimomostrandoohistóricodetransaçõesdeumaConta.Fazsentidocriarumobjetodotipo

HistoricosemumaConta?

Agora, além de funcionar como esperado, nosso código não permite criar uma conta sem osatributosquedefinimosanteriormente.Discutacomseuscolegaseinstrutorasvantagensdaorientaçãoaobjetosatéaqui.

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

JáconheceoscursosonlineAlura?

8.13EXERCÍCIO:PRIMEIRACLASSEPYTHON 109

Page 117: Python e Orientação a Objetos

CAPÍTULO9

Umdosproblemasmaissimplesquetemosnonossosistemadecontaséqueométodosaca()permite

sacarmesmoqueosaldosejainsuficiente.Aseguir,vocêpodelembrarcomoestáaclasseConta:

classConta:

def__init__(self,numero,titular,saldo,limite=1000.0):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

#outrosmétodos

defsaca(self,valor):self.saldo-=valor

Vamostestarnossocódigo:

minha_conta=Conta('123-4','João',1000.0,2000.0)minha_conta.saca(500000)

Olimitedesaqueéultrapassado.Podemosincluirumifdentrodométodosaca()paraevitara

situação que resultaria em uma conta em estado inconsistente, com seu saldo menor do que zero.Fizemosissonocapítulodeorientaçãoaobjetosbásica.

Apesardemelhorarbastante,aindatemosumproblemamaisgrave:ninguémgarantequeousuáriodaclassevai sempreutilizarométodoparaalterarosaldodaconta.Ocódigoaseguiralteraosaldodiretamente:

minha_conta=Conta('123-4','João',1000.0)minha_conta.saldo=-200

Comoevitarisso?Umaideiasimplesseriatestarsenãoestamossacandoumvalormaiorqueosaldotodavezqueformosalterá-lo.

minha_conta=Conta('123-4','joão',1000.0)novo_saldo=-200

if(novo_saldo<0):print("saldoinválido")else:minha_conta.saldo=novo_saldo

MODIFICADORESDEACESSOEMÉTODOSDECLASSE

110 9MODIFICADORESDEACESSOEMÉTODOSDECLASSE

Page 118: Python e Orientação a Objetos

Essecódigoiriaserepetiraolongodetodanossaaplicação,oupior,alguémpodeesquecerdefazeressacomparaçãoemalgummomento,deixandoacontaemumasituaçãoinconsistente.Amelhorformaderesolver issoseriaforçarquemusaaclasseContaa invocarométodosaca(), para assimnão

permitiroacessodiretoaoatributo.

EmlinguagenscomoJavaeC#,bastadeclararqueosatributosnãopossamseracessadosdeforadaclasse utilizando a palavra chave private. Em orientação a objetos, é prática quase que obrigatóriaprotegerseusatributoscomprivate.Cadaclasseéresponsávelporcontrolarseusatributos,portantoeladevejulgarseaquelenovovaloréválidoounão.Eestavalidaçãonãodevesercontroladaporquemestáusando a classe, e sim por ela mesma, centralizando essa responsabilidade e facilitando futurasmudançasnosistema.

O Python não utiliza o termoprivate, que é ummodificador de acesso e também chamado demodificador de visibilidade. No Python, inserimos dois underscores ('__') ao atributo paraadicionarmosestacaracterística:

classPessoa:

def__init__(self,idade):self.__idade=idade

Dessamaneira,nãoconseguimosacessaroatributoidadedeumobjetodotipoPessoaforada

classe:

pessoa=Pessoa(20)pessoa.idadeTraceback(mostrecentcalllast):File"<stdin>",line1,in<module>AttributeError:'Pessoa'objecthasnoattribute'idade'

OinterpretadoracusaqueoatributoidadenãoexistenaclassePessoa.Masissonãogaranteque

ninguémpossaacessá-lo.NoPython,nãoexistematributos realmenteprivados, ele apenasalertaquevocênãodeveriatentaracessaresteatributo,oumodificá-lo.Paraacessá-lo,fazemos:

p._Pessoa__idade

Ao colocar o prefixo __ no atributo da classe, o Python apenas renomeia '__nome_do_atributo'para '_nomeda_Classe\_nome_do_atributo', como fez em __idade para _Pessoa__idade. Qualquerpessoaquesaibaqueosatributosprivadosnãosãorealmenteprivados,mas"desconfigurados",podelere atribuir umvalor ao atributo "privado" diretamente.Mas fazerpessoa._Pessoa__idade=20 é

consideradomápráticaepodeacarretaremerros.

Podemosutilizarafunçãodirparaverqueoatributo_Pessoa__idadepertenceaoobjeto:

dir(pessoa)['_Pessoa__idade','__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__',

9MODIFICADORESDEACESSOEMÉTODOSDECLASSE 111

Page 119: Python e Orientação a Objetos

'__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

Reparequenãoexistenenhumatributo__idadenoobjetopessoa.Agoravamostentaratribuirum

valorpara__idade:

pessoa.__idade=25

Epa,seráqueoPythondeveriadeixarissoocorrer?Vamosacessaravariávelnovamenteeverseamodificaçãorealmenteaconteceu:

print(pessoa._Pessoa__idade)#20

OqueaconteceuaquiéqueoPythoncriouumnovoatributo__idadeparaoobjetopessoajáqueéumalinguagemdinâmica.Vamosutilizarafunçãodirnovamenteparaverisso:

dir(pessoa)['_Pessoa__idade','__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__idade','__init__','__init_subclass__','__le__','__lt_''__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

Notequeumnovoatributo__idadeapareceu, jáquefoi inicializadoemtempodeexecuçãoeé

diferentedo__idadedaclasse.Issopodegerarmuitaconfusãoeerros!OPythontambémtemuma

maneiradelidarcomesteproblemaatravésdavariável__slots__,ondedefinimosumnúmerolimitadodeatributosqueveremosaseguir.

Nenhum atributo é realmente privado em Python, já que podemos acessá-lo pelo seu nome'desfigurado'. Muitos programadores Python não gostam dessa sintaxe e preferem usar apenas umunderscore '_' para indicar quando um atributo deve ser protegido. Ou seja, deve ser explícita essadesconfiguraçãodonome- feitapeloprogramadorenãopelo interpretador- jáqueofereceomesmoresultado.Eargumentamque'__'sãoobscuros.

Oprefixo comapenasumunderscore não tem significado para o interpretador quando usado emnome de atributos, mas entre programadores Python é uma convenção que deve ser respeitada. Oprogramadoralertaqueesseatributonãodeveseracessadodiretamente:

def__init__(self,idade):self._idade=idade

Umatributocomapenasumunderscoreéchamadodeprotegido,masquandousadosinaliza quedevesertratadocomoumatributo"privado",fazendocomqueacessá-lodiretamentepossaserperigoso.

As mesmas regras de acesso aos atributos valem para os métodos. É muito comum, e faz todosentido, que seus atributos sejam privados e quase todos seus métodos sejam públicos (não é umaregra!). Desta forma, toda conversa de um objeto com outro é feita por troca demensagens, isto é,

112 9MODIFICADORESDEACESSOEMÉTODOSDECLASSE

Page 120: Python e Orientação a Objetos

acessandoseusmétodos.Algomuitomaiseducadoquemexerdiretamenteemumatributoquenãoéseu.

Melhorainda!OdiaqueprecisarmosmudarcomoérealizadoumsaquenanossaclasseConta,

adivinheondeprecisaríamosmodificar?Apenasnométodosaca(),oquefazplenosentido.

Oquecomeçamosavernessecapítuloéaideiadeencapsular,istoé,'esconder'todososmembrosdeumaclasse(comovimosacima),alémdeescondercomofuncionamasrotinas(nocasométodos)donossosistema.

Encapsularéfundamentalparaqueseusistemasejasuscetívelamudanças:nãoprecisamosmudaruma regra de negócio em vários lugares,mas sim em apenas um único lugar, já que essa regra estáencapsulada.Oconjuntodemétodospúblicosdeumaclasseétambémchamadodeinterfacedaclasse,poisestaéaúnicamaneiraaqualvocêsecomunicacomobjetosdessaclasse.

Ounderscore _ alerta queninguémdevemodificar, nemmesmo ler, o atributo emquestão.Comisso,temosumproblema:comofazerparamostrarosaldodeumaConta,jáquenãodevemosacessá-

loparaleituradiretamente?

Precisamosentãoarranjarumamaneiradefazeresseacesso.Semprequeprecisamosarrumarumamaneira de fazer alguma coisa com um objeto, utilizamosmétodos! Vamos então criar ummétodo,digamospega_saldo(),pararealizaressasimplestarefa:

classConta:

#outrosmétodos

defpega_saldo(self):returnself._saldo

Paraacessarmososaldodeumaconta,podemosfazer:

minha_conta=Conta('123-4','joão',1000.0)minha_conta.deposita(100)minha_conta.pega_saldo()1100

Para permitir o acesso aos atributos (já que eles são 'protegidos') de uma maneira controlada, aprática mais comum é criar dois métodos, um que retorna o valor e outro que muda o valor. Aconvençãoparaessesmétodosemmuitaslinguagensorientadasaobjetosécolocarapalavragetousetantes do nome do atributo. Por exemplo, uma conta com saldo e titular fica assim, no caso dedesejarmosdaracessoaleituraeescritaatodososatributos:

classConta:

def__init__(self,titular,saldo):

9.1ENCAPSULAMENTO

9.1ENCAPSULAMENTO 113

Page 121: Python e Orientação a Objetos

self._titular=titularself._saldo=saldo

defget_saldo(self):returnself._saldo

defset_saldo(self,saldo):self._saldo=saldo

defget_titular(self):returnself._titular

defset_titular(self,titular):self._titular=titular

Gettersesetterssãousadosemmuitaslinguagensdeprogramaçãoorientadaaobjetosparagarantiroprincípiodoencapsulamentodedados.Oencapsulamentodedadosévistocomooagrupamentodedados com os métodos que operam nesses dados. Esses métodos são, obviamente, o getter pararecuperarosdadoseosetterparaalterarosdados.Deacordocomesseprincípio,osatributosdeumaclassesãotornadosprivadosparaocultá-loseprotegê-losdeoutrocódigo.

Infelizmente,éumacrençageneralizadaqueumaclassePythonadequadadeveencapsularatributosprivadosusandogettersesetters.Assimqueumdessesprogramadoresintroduziremumnovoatributo,elefarácomquesejaumavariávelprivadaecriará"automaticamente"umgettereumsetterparaessesatributos.

Os programadores de Java irão torcer o nariz quando lerem o seguinte: Amaneira pythônica deintroduzir atributos é torná-los públicos.Vamos explicar issomais tarde. Primeiro, demonstramos noexemploaseguir,comopodemosprojetarumaclasse,damesmamaneirausadanoJava,comgettersesettersparaencapsularumatributoprotegido:

classConta:

def__init__(self,saldo):self._saldo=saldo

defget_saldo(self):returnself._saldo

defset_saldo(self,saldo):self._saldo=saldo

Epodemosvercomotrabalharcomessaclasseeosmétodos:

conta1=Conta(200.0)conta2=Conta(300.0)conta3=Conta(-100.0)conta1.get_saldo()#200.0conta2.get_saldo()#300.0conta3.set_saldo(conta1.get_saldo()+conta2.get_saldo())conta3.get_saldo()#500.0

114 9.1ENCAPSULAMENTO

Page 122: Python e Orientação a Objetos

Oquevocêachadaexpressão"conta3.set_saldo(conta1.get_saldo()+conta2.get_saldo())"?Éfeio,nãoé?Émuitomaisfácilescreverumaexpressãocomoaseguinte:

conta3.saldo=conta1.saldo+conta2.saldo

Talatribuiçãoémaisfácildeescrevere,acimadetudo,maisfácilde lerdoqueaexpressãocomgettersesetters.VamosreescreveraclasseContadeummodoPythônico,semgetteresemsetter:

classConta:

def__init__(self,saldo):self.saldo=saldo

Neste caso, não há encapsulamento e não seria um problema.Mas o que acontece se quisermosmudar a implementação no futuro? O leitor atento deve ter reparado que no exemplo anteriordeclaramos uma variável do tipo Conta com saldo negativo, e isso não deveria acontecer. Para

evitarmosessasituação,osetteréjustificadoparaacrescentaraseguintevalidação:

classConta:

def__init__(self,saldo):self.saldo=saldo

defset_saldo(self,saldo):if(saldo<0):print("saldonãopodesernegativo")else:self.saldo=saldo

Podemostestarissocom:

conta1=Conta(200.0)print(conta1.saldo)#200.0conta2=Conta(300.0)print(conta2.saldo)#300.0conta3=Conta(100.0)conta3.set_saldo(-100.0)"saldonãopodesernegativo"print(conta3.saldo)#100.0

Mas há um problema, pois caso projetemos nossa classe com atributo público e sem métodos,acabamosquebrandoainterface:

conta1=Conta(100.0)conta1.saldo=-100.0

ÉporissoqueemJavarecomenda-sequeaspessoasusemsomenteatributosprivadoscomgettersesetters,paraquesejapossívelalteraraimplementaçãosemprecisaralterarainterface.OPythonofereceuma solução bastante parecida para este problema. A solução é chamada de properties. Mantemosnossosatributosprotegidosedecoramosnossosmétodoscomumdecoratorchamadoproperty.

9.1ENCAPSULAMENTO 115

Page 123: Python e Orientação a Objetos

Aclassecomumapropriedadeficaassim:

classConta:

def__init__(self,saldo=0.0):self._saldo=saldo

@propertydefsaldo(self):returnself._saldo

@saldo.setterdefsaldo(self,saldo):if(saldo<0):print("saldonãopodesernegativo")else:self._saldo=saldo

Um método que é usado para obter um valor (o getter) é decorado com @property, isto é,

colocamos essa linha diretamente acima da declaração do método que recebe o nome do próprioatributo.Ométodoquetemquefuncionarcomosetteré[email protected]ção

tivessesidochamadade"func",teríamosqueanotá[email protected].

PARASABERMAIS:DECORATOR

Umdecorador, oudecorator é um padrão de projeto de software que permite adicionar umcomportamento a umobjeto já existente em tempode execução, ou seja, agrega dinamicamenteresponsabilidades adicionais a um objeto. Esta solução traz uma flexibilidade maior, em quepodemosadicionarouremoverresponsabilidadessemquesejanecessárioeditarocódigo-fonte.

Umdecoradoréumobjetoinvocável,umafunçãoqueaceitaoutrafunçãocomoparâmetro(afunção decorada). O decorador pode realizar algum processamento com a função decorada edevolvê-laousubstituí-laporoutrafunção.Opropertyéumdecoradorquepossuimétodosextras,como um getter e um setter, e ao ser aplicado a um objeto retorna uma cópia dele com essasfuncionalidades:

@propertydeffoo(self):returnself._foo

éequivalentea:

deffoo(self)returnself._foofoo=property(foo)

Portanto, a função foo() é substituída pela propriedade property(foo). Então, se você usa

@foo.setter(),oquevocêestáfazendoéchamarométodoproperty().setter

116 9.1ENCAPSULAMENTO

Page 124: Python e Orientação a Objetos

Desta maneira, podemos chamar esses métodos sem os parênteses, como se fossem atributospúblicos.É uma formamais elegante de encapsular nossos atributos.Vamos testar criar uma conta edepoisatribuirumvalornegativoaosaldo:

conta=Conta(1000.0)conta.saldo=-300.0"saldonãopodesernegativo"

Vejaquetemosumresultadomuitomelhordoqueusargettersandsettersdiretamente.Chamamosoatributopelasuaspropriedades,quepodemcontervalidações,enossosatributosestãosinalizadoscomo'protegidos'atravésdo'_'.

Aindapodemosmodificarosaldo,eistodeveriaserfeitoatravésdosmétodospúblicossaca()e

deposita().Então,[email protected]équestionável.Devemosapenasmanipular

osaldoatravésdosmétodossaca()edeposita(),nãoprecisamosdapropertysaldo.setter.Isso

éumadecisãodenegócioespecífico.Oprogramadordeveficaralertaquantoaspropriedadessettersdeseusatributos,nemsempreelassãonecessárias.

É uma má prática criar uma classe e, logo em seguida, criar as propriedades para todos seusatributos. Você só deve criar properties se tiver real necessidade. Repare que nesse exemplo, apropriedadesetter do saldonãodeveria ter sido criada, já quequeremosque todosusemosmétodosdeposita()esaca().

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

Nossobancotambémquercontrolaraquantidadedecontasexistentesnosistema.Comopoderíamosfazerisso?Bom,acadainstânciacriada,deveríamosincrementaressetotal:

total_contas=0conta=Conta(300.0)total_contas=total_contas+1conta2=Conta(100.0)

VocêpodetambémfazerocursodatadessaapostilanaCaelum

9.2ATRIBUTOSDECLASSE

9.2ATRIBUTOSDECLASSE 117

Page 125: Python e Orientação a Objetos

total_contas=total_contas+1

Aquivoltaoproblemaderepetirummesmocódigoparatodaaplicação,alémdeterquelembrardeincrementaravariáveltotal_contas todavezapós instanciarumaConta.Comototal_contas

temvínculocomaclasseConta,eledeveserumatributocontroladopelaclasse,quedeveincrementá-lotodavezqueinstanciamosumobjeto,ouseja,quandochamamosométodo__init__():

classConta:

def__init__(self,saldo):self._saldo=saldoself._total_contas=self._total_contas+1

Masondeinicializamosavariável_total_contas?Nãofazsentidorecebermosporparâmetrono

__init__(),jáqueéaclassequedevecontrolaressenúmeroenãooobjeto.Seriainteressanteque

essavariávelfosseprópriadaclasse,sendoúnicaecompartilhadaportodososobjetosdamesma.Dessamaneira,quandoamesmafossemudadaatravésdeumobjeto,ooutroenxergariaomesmovalor.Parafazerisso,vamosinicializaravariávelnaclasse,portanto,foradométodo__init__():

classConta:

total_contas=0

def__init__(self,saldo):self._saldo=saldoself.total_contas+=1

Vejaquesaldo é umatributode instância, etotal_contas umatributode classe.Vamosfazerumtesteparaversenossototal_contasfuncionacomoesperado:

c1=Conta(100.0)print(c1.total_contas)#1c2=Conta(200.0)print(c2.total_contas)#1

Criamosduasinstâncias,emesmoassimototal_contasnãomudou.Issoaconteceporcontado

self.total_contasserdiferentedetotal_contasdaclasse.Comototal_contaséumavariável

daclasse,devemoschamá-lapelaclasse:

classConta:

total_contas=0

def__init__(self,saldo):self._saldo=saldoConta.total_contas+=1

Etestamos:

c1=Conta(100.0)print(c1.total_contas)#1

118 9.2ATRIBUTOSDECLASSE

Page 126: Python e Orientação a Objetos

c2=Conta(200.0)print(c2.total_contas)#2

Agoraobtemosoresultadoesperado.Tambémépossívelacessaresteatributodiretodaclasse:

Conta.total_contas2

Mas não queremos que ninguém venha a acessar nosso atributo total_contas e modificá-lo.

Portantovamostorná-lo'protegido'acrescentandoum'_':

classConta:

_total_contas=0

Dessamaneiraavisamososusuáriosdenossaclassequeesseatributodeveserconsiderado'privado'enãomodificado.Mascomoacessá-loentão?Vejaqueagora,aoacessarpelaclasseobtemosumerro:

Conta.total_contasTraceback(mostrecentcalllast):File<stdin>,line23,in<module>Conta.total_contasAttributeError:'Conta'objecthasnoattribute'total_contas'

Precisamoscriarummétodoparaacessaroatributo.Vamoscriaroget_total_contas:

classConta:

_total_contas=0

#__init__eoutrosmétodos

defget_total_contas(self):returnConta._total_contas

Funciona quando chamamos este método por um instância, mas quando fazemosConta.get_total_contas()ointerpretadorreclama,poisnãopassamosainstância:

c1=Conta(100.0)print(c1.get_total_contas())#1c2=Conta(200.0)print(c2.get_total_contas())#2Conta.get_total_contas()Traceback(mostrecentcalllast):File<stdin>,line17,in<module>Conta.get_total_contas()TypeError:get_total_contas()missing1requiredpositionalargument:'self'

Vejaqueoerroavisaquefaltapassaroargumentoself.Nãopodemoschamá-lo,poiselenãoestá

vinculado a qualquer instância de Conta. Além disso, ummétodo requer uma instância como seu

primeiroargumento:

c1=Conta(100.0)c2=Conta(200.0)

9.2ATRIBUTOSDECLASSE 119

Page 127: Python e Orientação a Objetos

Conta.get_total_contas(c1)#2

Passamosainstânciac1deContaefuncionou.Masessanãoéamelhormaneiradesechamar

ummétodo.Achamadanãoéclarae levaum tempopara ler eentenderoquea terceira linhadessecódigorealmentefaz.Vamosentãodeixardepassaro'self'comoargumentodeget_total_contas:

defget_total_contas():returnConta._total_contas

Mas dessa maneira não conseguimos acessar o método, já que todo método exige o argumentoself:

c1=Conta(100.0)c1.get_total_contas()Traceback(mostrecentcalllast):File<stdin>in<module>c1.get_total_contas()TypeError:get_total_contas()takes0positionalargumentsbut1wasgiven

E agora, o que fazer? Queremos ummétodo que seja chamado via classe e via instância sem anecessidadedepassarareferênciadesteobjeto.OPythonresolveissousandométodosestáticos.

Métodos estáticos nãoprecisamdeuma referência, não recebemumprimeiro argumento especial(self).Écomoumafunçãosimplesque,poracaso,residenocorpodeumaclasseemvezdeserdefinidanoníveldomódulo.

Para que um método seja considerado estático, basta adicionarmos um decorador, assim comofizemoscomaspropriedadesnocapítuloanterior.Odecoradorsechama@staticmethod:

@staticmethoddefget_total_contas():returnConta._total_contas

Testando,vemosquefuncionatantochamadoporuminstânciaquantopelaclasse:

c1=Conta(100.0)c1.get_total_contas()#1c2=Conta(200.0)c2.get_total_contas()#2Conta.get_total_contas()#2

Métodosestáticosnãodevemserconfundidoscommétodosdeclasse.Comoosmétodosestáticos,métodosdeclassenãosãoligadosàsinstâncias,massimaclasse.Oprimeiroparâmetrodeummétododeclasseéumareferênciaparaaclasse,istoé,umobjetodotipoclass,queporconvençãonomeamoscomo 'cls'. Eles podem ser chamados via instância ou pela classe e utilizam um outro decorador, o

9.3MÉTODOSDECLASSE

120 9.3MÉTODOSDECLASSE

Page 128: Python e Orientação a Objetos

@classmethod:

classConta:

_total_contas=0

def__init__(self):type(self)._total_contas+=1

@classmethoddefget_total_contas(cls):returncls._total_contas

Epodemostestar:

c1=Conta(100.0)c1.get_total_contas()#1c2=Conta(200.0)c2.get_total_contas()#2Conta.get_total_contas()#2

Noiníciopodeparecerconfusoqualusar:@staticmethodou@classmethod?Issonãoétrivial.

Métodos de classe servem para definir ummétodo que opera na classe, e não em instâncias. Já osmétodosestáticosutilizamosquandonãoprecisamosreceberareferênciadeumobjetoespecial(sejadaclasseoudeumainstância)efuncionacomoumafunçãocomum,semrelação.

Isso ficará mais claro quando avançarmos no aprendizado. No próximo capítulo discutiremosHerança,umconceito fundamental emOrientaçãoaObjetos.Veremosqueclassespodem ter filhaseaproveitarocódigodasclassesmães.Ummétododeclassepodemudaraimplementação,ouseja,podeserreescritopelaclassefilha.Jáosmétodosestáticosnãopodemserreescritospelasfilhas,jáquesãoimutáveisenãodependemdeumreferênciaespecial.

@CLASSMETHODX@STATICMETHOD

Alguns programadores não veem muito sentido em usar métodos estáticos, já que se vocêescreverumafunçãoquenãovaiinteragircomaclasse,bastadefini-lanomódulo.Outrosjácontraargumentam em outra via, considerando herança de classes que veremos em outro capítulo.Indicamos a leitura do artigo 'The Definitive Guide on How to Use Static, Class and AbstractMethods in Python' de Julien Danjou que pode ser acessado pelo link:https://julien.danjou.info/guide-python-static-class-abstract-methods/.

Aprendemos sobre encapsulamento, e vimos que é uma boa prática proteger nossos atributos

9.4PARASABERMAIS-SLOTS

9.4PARASABERMAIS-SLOTS 121

Page 129: Python e Orientação a Objetos

incluindooprefixounderscore emseusnomes, seguindoa convençãoutilizadapelosprogramadores.Alémdisso,utilizamospropertiesparaacessaremodificarnossosatributos.MascomoPythonéumalinguagemdinâmica,nada impedequeusuáriosdenossaclasseConta criematributos em tempode

execução,fazendo,porexemplo:

>>>conta.nome="minhaconta"

Essecódigonãoacusaerroenossacontaficaabertaamodificaçõesferindoasegurançadaclasse.Paraevitar isso,podemosutilizarumavariávelembutidanoPythonchamada__slots__, quepode

guardarumalistadeatributosdaclassedefinidospornós:

classConta:

__slots__=['_numero','_titular','_saldo','_limite']

def__init__(self,numero,titular,saldo,limite=1000.0):#inicializaçãodosatributos

#códigoomitido

Agora,quandotentamosadicionarumatributonaclasse,recebemosumerro:

>>>conta.nome="minha_conta"Traceback(mostrecentcalllast):File<stdin>,line1,in<module>AttributeError:'Conta'objecthasnoattribute'__dict__'

classConta:

__slots__=['_numero','_titular','_saldo','_limite']

def__init__(self,numero,titular,saldo,limite=1000.0):self._numero=numeroself._titular=titularself._saldo=saldoself._limite=limite

#restantedocódigo

conta.nome="minha_conta"

ReparequeoerroacusaqueaclasseContanãopossuioatributo__dict__.Aoatribuirumvalor

para__slots__,ointerpretadordoPythonvaientenderquequeremosexcluiro__dict__daclasse

Contanãosendopossívelcriaratributos,ouseja,impossibilitandoadicionaratributosaodicionárioda

classequeé responsávelporarmazenaratributosde instância.Portanto, tentarchamarvars(conta)

tambémvaigerarumerro:

>>>vars(conta)Traceback(mostrecentcalllast):File<stdin>,line1,in<module>TypeError:vars()argumentmusthave__dict__attribute

Embora__slots__ sejamuitoutilizadopara nãopermitir queusuários denossas classes criem

outrosatributos,essanãoésuaprincipalfunçãonemomotivodesuaexistência.Oqueaconteceéqueo

122 9.4PARASABERMAIS-SLOTS

Page 130: Python e Orientação a Objetos

__dict__ desperdiça muita memória. Imagine um sistema grande, com milhões de instâncias de

Conta-teríamos,consequentemente,milhõesdedicionáriosdeclassearmazenandoseusatributosde

instância.OPythonnãopodesimplesmentealocarumaquantidadeestáticadememórianacriaçãodeobjetosparaarmazenartodososatributos.Porisso,consomemuitamemóriaRAMsevocêcriarmuitosobjetos.

Para contornar este problema é que se usa o __slots__, e este é seu principal propósito. O

__slots__avisaoPythonparanãousarumdicionárioeapenasalocarespaçoparaumconjuntofixo

deatributos.

Programadoresviramumareduçãodequase40a50%nousodeRAMusandoessatécnica.

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

1. Adicioneomodificadordevisibilidadeprivado(doisunderscores:__)paracadaatributoemétodo

dasuaclasseConta.

classConta:

def__init__(self,numero,titular,saldo,limite=1000.0):self.__numero=numeroself.__titular=titularself.__saldo=saldoself.__limite=limite

TentecriarumaContaemodificaroulerumdeseusatributos"privados".Oqueacontece?

2. Sabendo que no Python não existem atributos privados, como podemos modificar e ler essesatributos?Éumaboapráticafazerisso?

Dica: teste os comandos print(conta.__numero) e print(conta._Conta__numero. O que

Seuslivrosdetecnologiaparecemdoséculopassado?

9.5EXERCÍCIOS:

9.5EXERCÍCIOS: 123

Page 131: Python e Orientação a Objetos

ocorre?

3. Modifiqueoacessopara'protegido'seguindoaconvençãodoPythonemodifiqueoprefixo__por

apenasumunderscore_. Criemétodos de acesso em sua classe Conta através do decorator

@property.

classConta:

def__init__(self,numero,titular,saldo,limite=1000.0):self._numero=numeroself._titular=titularself._saldo=saldoself._limite=limite

@propertydefsaldo(self):returnself._saldo

@saldo.setterdefsaldo(self,saldo):if(saldo<0):print("saldonãopodesernegativo")else:self._saldo=saldo

#restantedosmétodosescritosnoexercícioanterior

4. Crienovamenteumacontaeacesseemodifiqueseusatributos.Oquemudou?

Dica:tenteoscomandosnaseguinteordem:print(conta._numero),conta._numero='50'e

print(conta._numero).Oqueocorre?

5. Modifique sua classe Conta de modo que não seja permitido criar outros atributos além dos

definidosanteriormenteutilizando__slots__.

classConta:

__slots__=['_numero','_titular','_saldo','_limite']

def__init__(self,numero,titular,saldo,limite=1000.0):self._numero=numeroself._titular=titularself._saldo=saldoself._limite=limite

6. (Opcional)AdicioneumatributoidentificadornaclasseConta.Esseidentificadordeveterum

valorúnicoparacadainstânciadotipoConta.AprimeiraContainstanciadatemidentificador1,a

segunda2,eassimpordiante.

classConta:

identificador=1

def__init__(self,numero,titular,saldo,limite=1000.0):#códigoomitido

124 9.5EXERCÍCIOS:

Page 132: Python e Orientação a Objetos

self.identificador=Conta.identificadorConta.identificador+=1

9.5EXERCÍCIOS: 125

Page 133: Python e Orientação a Objetos

CAPÍTULO10

OPycharmé especialmenteútil durante a criaçãode códigoque envolveorientação aobjetos.Nestepróximo passo, criaremos umnovo projeto, desta vez para conter todo o conteúdo necessário para aimplementaçãodanossaaplicaçãodebanco.

Novamente no Pycharm, abra uma janela chamada Create Project aparecerá. É nela que

definimostodasasconfiguraçõesnecessárias.

Podemoscriarumnovoprojetoaqualquermomento.Parafazerisso,bastaclicaremFile->New

ProjectnomenusuperiordajanelaprincipaldoPyCharm.

Primeiro,especificamosonomedoprojeto-nonossocasoseráapenasbanco.NotequeoPyCharm

PYCHARMEORIENTAÇÃOAOBJETOS

10.1CRIANDOUMPROJETO

126 10PYCHARMEORIENTAÇÃOAOBJETOS

Page 134: Python e Orientação a Objetos

sugereumlocalpadrãoparasalvaroprojeto.VocêpodeaceitarestelocalouconfigurarmanualmentenocampoLocation.Vamosoptarpelocaminhopadrão.Aofazerisso,aIDEvaicriarumapastachamada

PyCharmProjectsnasuapastahome.

Após isso, escolhemos a versão do interpretador que usaremos no projeto. O PyCharm cria umambienteisoladodainstalaçãopadrãodosistemaoperacional(nocasodoLinuxeMacOS).Issoémuitoimportante e não causa concorrência com outras bibliotecas instaladas em seu computador. Por fim,clicamosemCreateenossoprojetoécriado.

Nosso projeto tem uma estrutura padrão. A pasta venv é o ambiente isolado do sistema

operacional.Nela contémaversãodo interpretadorPythonque selecionamosna criaçãodoprojeto eseus módulos embutidos (builtins) - você pode checar isso na pasta lib . A qualquer momento

podemosincluirnovasbibliotecasaoprojeto.

Vamos iniciar nosso projeto criando a classeConta.Antes, precisamos criar uma pasta raiz do

projeto.VáemFile->New->Directory.

Definimosumnomeparanossodiretórioe clicamosem 'OK'.Vamoscriarumdiretóriochamadosrc:

10.1CRIANDOUMPROJETO 127

Page 135: Python e Orientação a Objetos

Paradefini-locomopastaraiz,vamosclicarcomobotãodireitodomousenodiretório,navegaratéMarkDirectoryaseescolherSourcesRoot.Vocênotaráqueapastamudadacorcinzaparaazul.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Agoravamoscriaroarquivoconta.pyqueconteránossaclasseConta.Paraisso,cliquecomo

Agoraéamelhorhoradeaprenderalgonovo

10.2CRIANDOUMACLASSE

128 10.2CRIANDOUMACLASSE

Page 136: Python e Orientação a Objetos

botãodireitodomouseemsrceváemNew->PythonFile:

Digiteonomedoarquivoecliqueem'OK'.Vamosnomearoarquivocomoconta:

Umanovaabavaiaparecercomonomedomóduloquecriamos,àdireitadomenudenavegaçãodoprojeto. Vamos começar a escrever o código de nossa classe Conta. Você vai notar que quando

começamosadigitarapalavraclassoPyCharmvaiteoferecersugestõesparavocêescolher.Comece

escrevendoapalavraclass(exemplo:'cl')eelevaiterminardedigitarpravocê:

Vamos agora escrever ométodo__init__().Aqui tambémoPycharmvai nos ajudar.Escreva

apenas"defi"eoPyCharmvaitedarnovamentesugestões:

10.2CRIANDOUMACLASSE 129

Page 137: Python e Orientação a Objetos

Escolhaaprimeiraopção__init__(self)eaperteENTER.Vaigerarocódigo:

classConta:

def__init__(self):

FaçaométodoinicializadorrecebercomoparâmetroosvaloresdoatributosdeumaConta,ouseja,

receberonumero,titular,saldoelimite:

classConta:

def__init__(self,numero,titular,saldo,limite=1000.0):self.numero=numeroself.titular=titularself.saldo=saldoself.limite=limite

Agoravamostestarnossaclasse.OPycharmpossuiumconsoledoPythonembutido,paraabrí-lováemTools->PythonConsole.Vocêvainotarquea janeladoconsolevaiabrirabaixodoarquivo

conta:

Vamosimportaromódulocontacomocomandofromsrc.contaimportConta, instanciar

umaContaeacessarseusatributos:

10.3EXECUTANDOCÓDIGO

130 10.3EXECUTANDOCÓDIGO

Page 138: Python e Orientação a Objetos

Reparequeoconsoletambémpossuiaferramentadeautocomplete.Parareiniciá-lobastaclicarnoprimeiroíconedomenuesquerdodoconsole.

AIDEtambémpermiteabriroterminaleusaromodointerativoparatestes.OatalhoparaabriroterminaléALT+F12.OPythonConsoledoPycharmémaisaconselhávelpara issoeo terminalé

maisutilizadoparainstalarnovaslibsaoseuprojeto.

Masusaremosoconsoleapenasparatestes.Vamosexecutarocódigodiretamentedonossoarquivoconta. Para isso precisamos acrescentar a condicional if __name__ == '__main__': e fazer os

mesmostestesquefizemosnoconsoledentrodestacondiçãoif.Bastavocêescrever"main"edigitar

ENTERqueoPycharmcriaacondicionalparavocê:

if__name__=='__main__':

AgoravamosinstanciareimprimirosatributosdeumaContacomofizemosutilizandooPython

Console.Nãoesqueçadeutilizarafunçãoprintnahorademostrarosatributos,jáquenãoestamosmaisnomodointerativo:

if__name__=='__main__':conta=Conta('123-4','João',1000.0)print(conta.numero)print(conta.titular)print(conta.saldo)print(conta.limite)

ParaexecutarváemRun->Runoucliquecomobotãodireitodomousenointeriordoarquivo

conta e escolha a opçãoRun'conta'.Ou ainda, digite o atalho CTRL+Shift+F10 que vai ter o

mesmoefeito.Depoisdeterrodadopelaprimeiravez,paravocêrodarnovamentebastaclicarnoíconedeumapequenasetaverdenomenusuperiordaIDE:

10.3EXECUTANDOCÓDIGO 131

Page 139: Python e Orientação a Objetos

Vamos criar nosso primeiro método. Primeiro, dentro da condicional main , vamos digitar a

seguintelinhadecódigo:

conta.deposita(100.0)

Seexecutarmosessecódigo,onossoprogramaquebrajáqueaclasseConta aindanãopossuio

métododeposita().Paracriá-lo,coloqueocursordomousenapalavra"deposita"euseoatalhoALT

+ENTER,váriassugestõesdoPycharmirãoaparecer.CliqueemAddmethoddeposita()toclass

Conta:

EoPycharmvaicriaradeclaraçãodométodoparavocê:

Viucomoéfácilerápido?Agorabastatrocarapalavraparamporvalor,apagarapalavrapass

eadicionaraimplementaçãodométodo:

10.4CRIANDOMÉTODOS

132 10.4CRIANDOMÉTODOS

Page 140: Python e Orientação a Objetos

defdeposita(self,valor):self._saldo+=valor

No próximo exercício, vamos criar nosso primeiro projeto do banco e nossa classe Conta

utilizandooPycharmepraticaroqueaprendemosdeorientaçãoaobjetosesobreaIDEatéaqui.

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

1. AbraoPycharmeváemFile->NewProject.Ajanelaabaixovaiaparecer.Troqueapalavra

untitledpelonomedonossoprojetoqueserábanco:

EditoraCasadoCódigocomlivrosdeumaformadiferente

10.5EXERCÍCIO-CRIANDOPROJETOBANCONOPYCHARM

10.5EXERCÍCIO-CRIANDOPROJETOBANCONOPYCHARM 133

Page 141: Python e Orientação a Objetos

VerifiqueseaversãodoPythonestácorretaemBaseinterpreterecliqueemOK.

2. Nomenuesquerdovaiapareceraestruturadoprojeto.Vamosdefinirumapasta raizonde ficarãonossosarquivosdecódigopython.CliquecomobotãodireitonapastabancoeescolhaaopçãoNewFolder.Umanovajanelavaiaparecerparavocêentrarcomonomedodiretório,digitesrceOK:

3. Após isso, clicamos com o botão direito na pasta src e selecionamos Mark Directory as ->SourcesRoots para avisar o PyCharmque esta pasta será umdiretório fonte de nosso projeto,

ondeficarãonossosarquivos.py.

Reparequeapastaficarádacorazuldepoisdeexecutardestaação:

4. Agora vamos criar nossa classe Conta que ficará no arquivo conta.py. Vamos fazer isso

utilizandoumatalhodoPyCharm.ColoqueocursordomousenapastasrcedigiteALT+Insert.EscolhaaopçãoPythonFile.Umanovajanelavaiabrir,digite"conta"ecliqueemOK.

134 10.5EXERCÍCIO-CRIANDOPROJETOBANCONOPYCHARM

Page 142: Python e Orientação a Objetos

5. Oarquivoseráabertoaesquerda.Vamoscomeçaracriarnossaclasse.AoescrevermosafunçãoinitaprópriaIDEvaimostrarasopçõesemumajanela,bastaclicarqueeleauto-completaparavocêjácomaargumento'self'.AdicioneosatributosdeumaContacomofizemosnoexercíciodocapítulo

anterior:

classConta:

def__init__(self,numero,titular,saldo,limite):self._numero=numeroself._titular=titularself._saldo=saldoself._limite=limite

Aproveiteacrieaspropertiesdecadaatributo.AbusedoCTRL+ESPAÇOparaaIDEautocompletar

paravocêedoALT+ENTERparasugestões.

6. Em seguida criamos a condicional para que o PyCharm rode algumas linhas de código caso o__name__sejaiguala__main__,ouseja,oprogramaprincipal.OPycharmtambémfacilitaesta

criação,bastadigitarapalavra'main'eapertarCTRL+ESPAÇOqueaestruturadoiféconstruída

paravocê:

if__name__=='__main__':

Vamoscriarumanovacontaeimprimirotitular:

if__name__=='__main__':conta=Conta('123-4','joão',1200.0,1000.0)print(conta.titular)

7. Pararodar,bastaclicarcomobotãodireitodomouseeescolheraopçãoRun'conta'ouutilizaro

atalhoCtrl+Shift_F10.Ou ainda escolher a opção da barra de ferramentas com o símbolo de

playdacorverde.Oresultadovaiaparecernoconsole,najanelainferiordaIDE.

10.5EXERCÍCIO-CRIANDOPROJETOBANCONOPYCHARM 135

Page 143: Python e Orientação a Objetos

8. Crieosmétodosdeposita(),saca(),extrato() etransfere_para() como fizemosno

últimoexercício.AproveiteosrecursosdaIDEqueaprendemosparacriartodosessesmétodos.Apropriedadesetterdosaldoénecessária?

9. Crieduascontasetesteosmétodosquevocêcriounoexercícioanterior.

10. (Opcional)Crieumarquivopythonchamadocliente.pyecrieaclasseClientecomnome,

sobrenomeecpf.TesteocódigopassandoumclientecomotitulardeumConta.Aproveitee

adicionealgunsmétodosaela.

VejaqueaIDEfacilitabastantenahoradodesenvolvimentoeganhamostempotambémrodandooscriptdiretamentenoPyCharm.

136 10.5EXERCÍCIO-CRIANDOPROJETOBANCONOPYCHARM

Page 144: Python e Orientação a Objetos

CAPÍTULO11

Como toda empresa, nosso banco possui funcionários. Um funcionário tem um nome, um cpf e umsalário.VamosmodelaraclasseFuncionario:

classFuncionario:

def__init__(self,nome,cpf,salario):self._nome=nomeself._cpf=cpfself._salario=salario

#outrosmétodosepropriedades

Alémdeumfuncionáriocomum,hátambémoutroscargos,comoosgerentes.Osgerentesguardama mesma informação que um funcionário comum, mas possuem outras informações, além de terfuncionalidadesumpoucodiferentes.Umgerentenonossobancopossuitambémumasenhanuméricaquepermiteoacessoaosistemainternodobanco,alémdonúmerodefuncionáriosqueelegerencia:

classGerente:

def__init__(self,nome,cpf,salario,senha,qtd_gerenciados):self._nome=nomeself._cpf=cpfself._salario=salarioself._senha=senhaself._qtd_gerenciados=qtd_gerenciados

defautentica(self,senha):ifself._senha==senha:print("acessopermitido")returnTrueelse:print("acessonegado")returnFalse

#outrosmétodos(comunsaumFuncionario)

Se tivéssemos um outro tipo de funcionário que tem características diferentes do funcionáriocomum,precisaríamoscriarumaoutraclasseecopiarocódigonovamente.

Além disso, se um dia precisarmos adicionar uma nova informação para todos os funcionários,precisaremospassarportodasasclassesdefuncionárioeadicionaresseatributo.Oproblemaacontecenovamentepornãocentralizarmosasinformaçõesprincipaisdofuncionárioemumúnicolugar!

HERANÇAEPOLIMORFISMO

11.1REPETINDOCÓDIGO?

11HERANÇAEPOLIMORFISMO 137

Page 145: Python e Orientação a Objetos

Existeumjeitoderelacionarmosumaclassedetalmaneiraqueumadelasherdatudoqueooutratem. Isto é uma relação de herança, uma relação entre classe 'mãe' e classe 'filha'. No nosso caso,gostaríamosdefazercomqueGerentetivessetudoqueumFuncionariotem,gostaríamosqueela

fosseumaextensãodeFuncionario.Fazemosissoacrescentandoaclassemãeentreparentesesjuntoa

classefilha:

classGerente(Funcionario):

def__init__(self,senha,qtd_funcionarios):self._senha=senhaself._qtd_funcionarios=qtd_funcionarios

defautentica(self,senha):ifself._senha==senha:print("acessopermitido")returnTrueelse:print("acessonegado")returnFalse

TodomomentoquecriarmosumobjetodotipoGerentequeremosqueesteobjetotambémherde

osatributosdefinidosnaclasseFuncionario,poisumGerenteéumFuncionário.

ComoaclasseGerente já possui ummétodo__init__() comoutros atributos, ométododa

classe Funcionario é sobrescrito pelo Gerente. Se queremos incluir os mesmos atributos deinstância de Funcionario em um Gerente , devemos chamar o método __init__() de

Funcionariodentrodométodo__init__()deGerente:

classGerente(Funcionario):

def__init__(self,senha,qtd_funcionarios):Funcionario.__init__(nome,cpf,salario)self._senha=senhaself._qtd_funcionarios=qtd_funcionarios

defautentica(self,senha):ifself._senha==senha:print("acessopermitido")returnTrueelse:print("acessonegado")returnFalse

138 11.1REPETINDOCÓDIGO?

Page 146: Python e Orientação a Objetos

DizemosqueaclasseGerenteherdatodososatributosemétodosdaclassemãe,nonossocaso,a

Funcionario.ComoPythontemtipagemdinâmica,precisamosgarantirissoatravésdoconstrutorda

classe.Alémdesenhaeqtd_funcionariospassamostambémosatributosnome,cpfesalario

quetodofuncionáriotem:

classGerente(Funcionario):

def__init__(self,nome,cpf,salario,senha,qtd_funcionarios):self._senha=senhaself._qtd_funcionarios=qtd_funcionarios

Como estes são atributos de um Funcionario e não queremos repetir o código do método

__init__()deFuncionariodentrodaclasseGerente, podemos chamar estemétododa classe

mãecomofizemosnoexemploacimaoupodemosutilizarummétododoPythonchamadosuper():

classGerente(Funcionario):

def__init__(self,nome,cpf,salario,senha,qtd_funcionarios):super().__init__(nome,cpf,salario)self._senha=senhaself._qtd_funcionarios=qtd_funcionarios

Para sermais preciso, ela também herda os atributos emétodos 'privados' de Funcionario. O

super() é usado para fazer referência a superclasse, a classe mãe - no nosso exemplo a classe

Funcionario.

11.1REPETINDOCÓDIGO? 139

Page 147: Python e Orientação a Objetos

PARASABERMAIS:SUPERESUBCLASSE

AnomenclaturamaisencontradaéqueFuncionarioéasuperclassedeGerente,eGerenteéasubclasse deFuncionario.Dizemos tambémque todoGerenteéum Funcionario.Outra forma édizerqueFuncionarioéaclassemãedeGerenteeGerenteéaclassefilhadeFuncionario.

Da mesma maneira, podemos ter uma classe Diretor que estenda Gerente , e a classe

Presidente pode estender diretamente de Funcionario. Fique claro que essa é uma relação de

negócio.SeDiretor vai estender deGerente ou não, vai depender, para você,Diretor é um

Gerente?

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

Todofimdeano,osfuncionáriosdonossobancorecebemumabonificação.Osfuncionárioscomunsrecebem10%dovalordosalárioeosgerentes,15%.

JáconheceoscursosonlineAlura?

11.2REESCRITADEMÉTODOS

140 11.2REESCRITADEMÉTODOS

Page 148: Python e Orientação a Objetos

VamosvercomoficaaclasseFuncionario:

classFuncionario:

def__init__(self,nome,cpf,salario):self._nome=nomeself._cpf=cpfself._salario=salario

#outrosmétodoseproperties

defget_bonificacao(self):returnself._salario*0.10

SedeixarmosaclasseGerentecomoelaestá,elavaiherdarométodoget_bonificacao()

gerente=Gerente('José','222222222-22',5000.0,'1234',0)print(gerente.get_bonificacao())

O resultado aqui será 500.Não queremos essa resposta, pois o gerente deveria ter 750 de bônusnesse caso. Para consertar isso, uma das opções seria criar um novo método na classe Gerente,

chamado,porexemplo,get_bonificacao_do_gerente().Oproblemaéque teríamosdoismétodos

emGerente,confundindobastantequemforusaressaclasse,alémdequecadaumgerenciariauma

respostadiferente.

No Python, quando herdamos um método, podemos alterar seu comportamento. Podemosreescrever(sobrescrever,override)estemétodo,assimcomofizemoscomo__init__:

classGerente(Funcionario):

def__init__(self,nome,cpf,salario,senha,qtd_gerenciaveis):super().__init__(nome,cpf,salario)self._senha=senhaself._qtd_gerenciaveis=qtd_gerenciaveis

defget_bonificacao(self):returnself._salario*0.15

#metodoseproperties

Agora, ométodo está correto para oGerente. Refaça o teste e veja que o valor impresso é o

correto(750):

gerente=Gerente('José','222222222-22',5000.0,'1234',0)print(gerente.get_bonificacao())

Utilizeométodovars()paraacessarosatributosdeGerenteeverqueaclasseherdatodosos

atributosdeFuncionario:

funcionario=Funcionario('João','111111111-11',2000.0)print(vars(funcionario))

gerente=Gerente('José','222222222-22',5000.0,'1234',0)print(vars(gerente))

11.2REESCRITADEMÉTODOS 141

Page 149: Python e Orientação a Objetos

Saída:

{'_salario':2000.0,'_nome':'João','_cpf':'111111111-11'}{'_cpf':'222222222-22','_salario':5000.0,'_nome':'José','_qtd_funcionarios':0,'_senha':'1234'}

Depoisde reescrito, nãopodemosmais chamarométodoantigoque foraherdadoda classemãe,poisrealmentealteramososeucomportamento.Todavia,podemosinvocá-lonocasodeestarmosdentrodaclasse.

ImaginequeparacalcularabonificaçãodeumGerente, devemos fazer igual ao cálculodeum

Funcionario,adicionando1000.0reais.Poderíamosfazerassim:

classGerente(Funcionario):

def__init__(self,senha,qtd_gerenciaveis):self._senha=senhaself._qtd_gerenciaveis=qtd_gerenciaveis

defget_bonificacao():returnself._salario*0.10+1000.0

#métodoseproperties

Aqui teríamos um problema: o dia que o get_bonificacao() do Funcionario mudar,

precisaremosmudarométododoGerente para acompanhar a novabonificação.Para evitar isso, o

get_bonificacao()doGerentepodechamarodoFuncionarioutilizandoométodosuper().

classGerente(Funcionario):

def__init__(self,senha,qtd_gerenciaveis):self._senha=senhaself._qtd_gerenciaveis=qtd_gerenciaveis

defget_bonificacao():returnsuper().get_bonificacao()+1000

#métodoseproperties

Essa invocaçãovaiprocurarométodocomonomeget_bonificacao() de uma superclassede

Gerente.Nocaso,elelogovaiencontraressemétodoemFuncionario.

Essaéumapráticacomum,poisemmuitoscasosométodoreescritogeralmentefazalgoamaisqueométododaclassemãe.Chamarounãoométododecimaéumadecisãoedependedoseuproblema.Algumasvezes,nãofazsentidoinvocarométodoquereescrevemos.

Para escrever uma classe utilizando o Python 2, é preciso acrescentar a palavra 'object' quandodefinimosumaclasse:

classMinhaClasse(object):

11.3INVOCANDOOMÉTODOREESCRITO

142 11.3INVOCANDOOMÉTODOREESCRITO

Page 150: Python e Orientação a Objetos

pass

Issoaconteceporquetodaclasseéfilhadeobject -queéchamadaamãede todasasclasses.NoPyhton, todaclasseherdadeobject.NoPython3,nãoprecisamosacrescentaroobject,masnãoquerdizerqueestaclasseeaherançanãoexistam,apenasqueamesmaé implícita.Quandocriamosumaclassevaziaeutilizamosométododir()parachecaralistadeseusatributos,reparamosqueelanãoé

vazia:

classMinhaClasse():pass

if__name__=='__main__':mc=MinhaClasse()print(dir(mc))

Gerandoasaída:

['__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__module__''__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

Todos estes atributos são herdados da classe object e podemos reescrever qualquer um deles nanossa subclasse. Todos eles são os conhecidos métodos 'mágicos' (começam e iniciam com doisunderscores,eporestemotivo,tambémchamadosdedunders).

Vimosocomportamentodo__init__(),__new__()edo__dict__.Outrosmétodosmágicos

famosossão__str__()e__repr__()-métodosqueretornamarepresentaçãodoobjetocomouma

string.Quandochamamosprint(mc)temosasaída

<__main__.MinhaClasseobjectat0x7f11c1f59a58>

Esseéomodelopadrãode impressãodeumobjeto, implementadonaclasseobject.A função

print() na verdade usa a string definida pelo método __str__() de uma classe. Vamos

reescreverestemétodo:

classMinhaClasse:

def__str__(self):return'<Instânciade{};endereço:{}>'.format(self.__class__.__name__,id(self))

Agora,quandoexecutamosprint(mc),asaídaé:

<InstânciadeMinhaClasse;endereço:0x7f11c1f59a58>

OPythonsemprechamaométodo__str__()quandoutilizaafunçãoprint() emumobjeto.

Novamente,estamosutilizandoreescritademétodos.

Ométodo__repr__()tambémretornaumastring,epodemosutilizarafunçãorepr()para

checarseuretorno:

11.3INVOCANDOOMÉTODOREESCRITO 143

Page 151: Python e Orientação a Objetos

classMinhaClasse():pass

if__name__=='__main__':mc=MinhaClasse()print(repr(mc))

Quevaigeraramesmasaídapadrãodo__str__():

<__main__.MinhaClasseobjectat0x7f11c1f59a58>

Masdiferentedo__str__(), não é comumsobrescrever estemétodo.Ele é sobrescrito quando

precisamosutilizá-lojuntocomafunçãoeval()doPython.Afunçãoeval()recebeumastringe

tentaexecutaressastringcomoumcomandodoPython.Vejaumexemplodeuso:

>>>x=1>>>eval("x+1")2

Vamosaumexemploutilizandoclasses:

classPonto:

def__init__(self,x,y):self.x=xself.y=y

def__str__(self):return"({},{})".format(self.x,self.y)

def__repr__(self):return"Ponto({},{})".format(self.x+1,self.y+1)

if__name__=='__main__':p1=Ponto(1,2)p2=eval(repr(p1))

print(p1)print(p2)

Seexecutarmosocódigoacima,temos:

(1,2)(2,3)

Reparequeutilizamosafunçãorepr()passandoumainstânciadePonto.OPythonvaichamar

entãoométodo__repr__()daclassePonto,queretornaastring"Ponto(2,3)"jáquep1.x=1

ep1.y=2.Aopassá-ladeargumentoparaafunçãoeval(),teremos:p2=eval('Ponto(2,3)).

Comoafunçãoeval()vai tentarexecutaressastring comoumcomandoPythonválidoele terá

sucesso,eportantop2seráumanovainstânciadaclassePontocomp2.x=2ep2.y=3.

Para concluir, é importante entender que tanto__str__() quanto__repr__() retornam uma

string que representaoobjeto,mas compropósitosdiferentes.Ométodo__str__() é utilizado

para apresentar mensagens para os usuários da classe, de maneira mais amigável. Já o método

144 11.3INVOCANDOOMÉTODOREESCRITO

Page 152: Python e Orientação a Objetos

__repr__()éusadopararepresentaroobjetodemaneiratécnica,inclusivepodendoutilizá-locomo

comandoválidodoPython,comovimosnoexemplodaclassePonto.

Os métodos mágicos são úteis, pois permitem que os objetos de nossas classes possuam umainterface de acesso semelhante aos objetos embutidos do Python. O método __add__(), por

exemplo, serve para executar a adição de dois objetos e é chamada sempre quando fazemos aoperaçãodeadição(obj+obj)utilizandoooperador'+'.Porexemplo,quandofazemos1+1no

Python,oqueointerpretadorfazéchamarométodo__add__()daclasseint.Vimosqueuma

listtambémimplementaométodo__add__(),jáqueaoperaçãodeadiçãoédefinidaparaesta

classe:

>>>lista=[1,2,3]>>>lista+[4,5][1,2,3,4,5]

O mesmo ocorre para as operações de multiplicação, divisão, módulo e potência que sãodefinidas pelos métodos mágicos __mul__() , __div__() , __mod__() e __pow__() ,

respectivamente.

Podemos definir cada uma dessas operações em nossas classes sobrescrevendo taismétodosmágicos. Além desses, o Python possui muitos outros que você pode acessar aqui:https://docs.python.org/3/reference/datamodel.html#Basic_customization

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

11.4PARASABERMAIS-MÉTODOSMÁGICOS

VocêpodetambémfazerocursodatadessaapostilanaCaelum

11.5POLIMORFISMO

11.4PARASABERMAIS-MÉTODOSMÁGICOS 145

Page 153: Python e Orientação a Objetos

OqueguardaumavariáveldotipoFuncionario?UmareferênciaparaumFuncionario,nuncao

objetoemsi.

Naherança,vimosquetodoGerenteéumFuncionario,poiséumaextensãodeste.Podemos

nos referir a um Gerente como sendo um Funcionario . Se alguém precisa falar com um

Funcionariodobanco,podefalarcomumGerente!Porque?PoisGerenteéumFuncionario.Essaéasemânticadaherança.

Polimorfismo é a capacidade de um objeto poder ser referenciado de várias formas (cuidado,polimorfismonãoquerdizerqueoobjetoficasetransformando,muitopelocontrário,umobjetonascedeumtipoemorredaqueletipo,oquepodemudaréamaneiracomonosreferimosaele).

A situação que costuma aparecer é a que temos um método que recebe um argumento do tipoFuncionario:

classControleDeBonificacoes:

def__init__(self,total_bonificacoes=0):self._total_bonificacoes=total_bonificacoes

defregistra(self,funcionario):self._total_bonificacoes+=funcionario.get_bonificacao()

@propertydeftotal_bonificacoes(self):returnself._total_bonificacoes

Epodemosfazer:

if__name__=='__main__':funcionario=Funcionario('João','111111111-11',2000.0)print("bonificacaofuncionario:{}".format(funcionario.get_bonificacao()))

gerente=Gerente("José","222222222-22",5000.0'1234',0)print("bonificacaogerente:{}".format(gerente.get_bonificacao()))

controle=ControleDeBonificacoes()controle.registra(funcionario)controle.registra(gerente)

print("total:{}".format(controle.total_bonificacoes))

quegeraasaída:

bonificacaofuncionario:200.0bonificacaogerente:1500.0total:1700.0

Repareque conseguimospassar umGerente para ummétodo que "recebe" umFuncionario

como argumento. Pense como numa porta na agência bancária com o seguinte aviso: "Permitida aentrada apenas de Funcionários". Um gerente pode passar nessa porta? Sim, pois Gerente é umFuncionario.

146 11.5POLIMORFISMO

Page 154: Python e Orientação a Objetos

Qual será o valor resultante? Não importa que dentro do método registra() do

ControleDeBonificacoesrecebaFuncionario.Quandoelereceberumobjetoquerealmenteéum

Gerente,oseumétodoreescritoseráinvocado.Reafirmando:nãoimportacomonosreferenciamosaumobjeto,ométodoqueseráinvocadoésempreoqueédele.

No dia emque criarmos uma classeSecretaria, por exemplo, que é filha deFuncionario,

precisaremosmudaraclasseControleDeBonificacoes?Não.BastaaclasseSecretariareescrever

osmétodosquelhepareceremnecessários.Éexatamenteesseopoderdopolimorfismo,juntamentecomareescritademétodo:diminuiroacoplamentoentreasclasses,paraevitarquenovoscódigosresultememmodificaçõeseminúmeroslugares.

ReparequequemcriouControleDeBonificacoespodenunca ter imaginadoacriaçãodaclasse

Secretaria ouEngenheiro. Contudo, não será necessário reimplementar esse controle em cada

novaclasse:reaproveitamosaquelecódigo.

Pensardestamaneiraemlinguagenscomtipagemestáticaéomaiscorreto,jáqueasvariáveissãotipadas e garantem, através do compilador, que o método só funcionará se receber um tipoFuncionario.Masnãoéoqueaconteceem linguagensde tipagemdinâmicacomoPython.Vamos

suporquetemosumaclassepararepresentarosclientesdobanco:

classCliente:

def__init__(self,nome,cpf,senha):self._nome=nomeself._cpf=cpfself._senha=senha

#métodoseproperties

Nada impede de registrarmos um Cliente em ControleDeBonificacoes. Vamos ver o que

acontece:

cliente=('Maria','333333333-33','1234')controle=ControleBonificacoes()controle.registra(cliente)

Saída:

File"<stdin>",line99,in<module>controle.registra(cliente)File"<stdin">,line67,inregistaself._total_bonificacoes+=funcionario.get_bonificacao()AttributeError:'Cliente'objecthasnoattribute'get_bonificacao'

VejaquelançaumAttibuteErrorcomamensagemdizendoqueClientenãopossuioatributo

get_bonificacao. Portanto, aqui não importa se o objeto recebidonométodoregistra() é um

Funcionario,masseelepossuiométodoget_bonificacao().

Ométodoregistra() utiliza ummétododa classeFuncionario e, portanto, funcionará com

11.5POLIMORFISMO 147

Page 155: Python e Orientação a Objetos

qualquer instância de uma subclasse de Funcionario ou qualquer instância de uma classe que

implementeométodoget_bonificacao().

Podemos evitar este erro verificando se o objeto passado possui ou não um atributoget_bonificacao()atravésdafunçãohasattr():

classControleDeBonificacoes:

def__init__(self,total_bonificacoes=0):self._total_bonificacoes=total_bonificacoes

defregistra(self,obj):if(hasattr(obj,'get_bonificacao')):self._total_bonificacoes+=obj.get_bonificacao()else:print('instânciade{}nãoimplementaométodoget_bonificacao()'.format(self.__class__.__name__))

#demaismétodos

A funçãohasattr() recebe dois parâmetros, o objeto e o atributo (na forma destring) - e

verificaseoobjetopossuiaqueleatributo,ouseja,seoatributoestácontidono__dict__doobjeto.

Então,fazemosapergunta:get_bonificacao()éatributodeFuncionario?Sesim,entranobloco

ifepodemoschamarométodotranquilamente,evitandoerros.

Agora,setentarmoschamarométodoregistra()passandoumClienterecebemosasaída:

'Cliente'objecthasnoattribute'get_bonificacao

Portanto,otipopassadoparaométodoregistra()nãoimportaaqui,esimseoobjetopassado

implementa ou não o método get_bonificacao() . Ou seja, basta que o objeto atenda a um

determinadoprotocolo.

ExisteumafunçãonoPythonquefuncionadeformasemelhantemasconsideraotipodainstância,éafunçãoisinstance().Aoinvésdepassarumainstância,passamosaclassenosegundoparâmetro.

classControleDeBonificacoes:

def__init__(self,total_bonificacoes=0):self.__total_bonificacoes=total_bonificacoes

defregistra(self,obj):if(isinstance(obj,Funcionario)):self.__total_bonificacoes+=obj.get_bonificacao()else:print('instânciade{}nãoimplementaométodoget_bonificacao()'.format(self.__class__.__name__))

MasessanãoéamaneiraPythônica.Vocêdeveescreverocódigoesperandosomenteumainterfacedoobjeto, não o tipo dele.A interface é o conjunto demétodos públicos de uma classe.No caso danossa classe ControleDeBonificacoes, o método registra() espera um objeto que possua o

métodoget_bonificacao(),enãoumobjetodotipoFuncionario.

148 11.5POLIMORFISMO

Page 156: Python e Orientação a Objetos

UmacaracterísticadelinguagensdinâmicascomoPythonéachamadaDuckTyping,atipagemdepato.Éumacaracterísticadeumsistemadetiposemqueasemânticadeumaclasseédeterminadapelasua capacidade de responder a alguma mensagem, ou seja, responder a determinado atributo (oumétodo).Oexemplocanônico(earazãodonome)éotestedopato:seeleseparececomumpato,nadacomoumpatoegrasnacomoumpato,entãoprovavelmenteéumpato.

Vejaoexemploabaixo:

classPato:defgrasna(self):print('quack!')

classGanso:defgrasna(self):print('quack!')

if__name__=='__main__':pato=Pato()print(pato.grasna())

ganso=Ganso()print(ganso.grasna())

Quegeraasaída:

quack!quack!

Vocêdeveescreverocódigoesperandosomenteumainterfacedoobjeto,nãoumtipodeobjeto.NocasodanossaclasseControleDeBonificacoes,ométodoregistra()esperaumobjetoquepossua

ométodoget_bonificacao()enãoapenasumfuncionário.

ODuckTypingéumestilodeprogramaçãoquenãoprocuraotipodoobjetoparadeterminarseeletema interface correta.Ao invésdisso, ométodoou atributo é simplesmente chamadoouusado ('separececomoumpatoegrasnacomoumpato,entãodeveserumpato').DuckTypingevitatestesusandoasfunçõestype(),isinstance()eatémesmoahasattr()-aoinvésdisso,deixaoerroestourar

nafrentedoprogramador.

AmaneiraPythônicaparagarantiraconsistênciadosistemanãoéverificarostiposeatributosdeumobjeto,maspressuporaexistênciadoatributonoobjetoetratarumaexceção,casoocorra,atravésdocomandotry/except:

try:self._total_bonificacoes+=obj.get_bonificacao()exceptAttributeErrorase:print(e)

Estamospedindoaointerpretadorpara tentarexecutara linhadecódigodentrodocomandotry

(tentar).Casoocorraalgumerro,elevaitrataresteerrocomocomandoexcepteexecutaralgo,como

11.6DUCKTYPING

11.6DUCKTYPING 149

Page 157: Python e Orientação a Objetos

imprimiroerro(similaraoexemplo).Nãosepreocupedeentenderosdetalhessobreestecódigoeousodotry/exceptnestemomento,teremosumcapítulosóparafalardeles.

O que é importante é que a maneira pythônica de se fazer é assumir a existência do atributo ecapturar(tratar)umexceçãoquandooatributonãopertenceraoobjetoeseguirofluxodoprograma.Porora,faremosestachecagemutilizandoafunçãohasattr().

HERANÇAVERSUSACOPLAMENTO

Notequeousodeherançaaumentaoacoplamentoentreasclasses,istoé,oquantoumaclassedependedeoutra.Arelaçãoentreclassemãeefilhaémuitoforteeissoacabafazendocomqueoprogramadordasclassesfilhas tenhaqueconhecera implementaçãodaclassemãeevice-versa-ficadifícilfazerumamudançapontualnosistema.

Por exemplo, imagine se tivermos quemudar algo na nossa classeFuncionario,masnão

quiséssemosquetodososfuncionáriossofressemamesmamudança.Precisaríamospassarporcadauma das filhas de Funcionario verificando se ela se comporta como deveria ou se devemos

sobrescreverotalmétodomodificado.

Esseéumproblemadaherança,enãodopolimorfismo,queresolveremosmaistarde.

Vamos ter mais de um tipo de conta no nosso sistema. Portanto, além das informações que játínhamosnaconta,temosagoraotipo:sequeremosumacontacorrenteouumacontapoupança.Alémdisso,cadaumadevepossuirumataxa.

1. AdicionenaclasseContaumnovométodochamadoatualiza()queatualizaacontadeacordo

comataxapercentual:

classConta:

#outrosmétodos

defatualiza(self,taxa):self._saldo+=self._saldo*taxa

2. CrieduassubclassesdaclasseConta:ContaCorrenteeContaPoupanca.Ambasterãoométodo

atualiza() reescrito: a ContaCorrente deve atualizar-se com o dobro da taxa e a

ContaPoupanca deve atualizar-se com o triplo da taxa. Além disso, a ContaCorrente deve

reescrever o método deposita() a fim de retirar uma taxa bancária de dez centavos de cada

depósito.

11.7EXERCÍCIO:HERANÇAEPOLIMORFISMO

150 11.7EXERCÍCIO:HERANÇAEPOLIMORFISMO

Page 158: Python e Orientação a Objetos

CrieaclasseContaCorrentenoarquivoconta.pyefaçacomqueelasejasubclasse(filha)daclasseConta.

classContaCorrente(Conta):pass

CrieaclasseContaPoupancanoarquivoconta.pyefaçacomqueelasejasubclasse(filha)daclasseConta:

classContaPoupanca(Conta):pass

Reescrevaométodoatualiza()naclasseContaCorrente,seguindooenunciado:

classContaCorrente(Conta):

defatualiza(self,taxa):self._saldo+=self._saldo*taxa*2

Reescrevaométodoatualiza()naclasseContaPopanca,seguindooenunciado:

classContaPoupanca(Conta):

defatualiza(self,taxa):self._saldo+=self._saldo*taxa*3

NaclasseContaCorrente,reescrevaométododeposita()paradescontarataxabancáriade

dezcentavos:

classContaCorrente(Conta):

defatualiza(self,taxa):self._saldo+=self._saldo*taxa*2

defdeposita(self,valor):self._saldo+=valor-0.10

3. Agora,testesuasclassesnoprópriomóduloconta.py.Acrescenteacondiçãoquandoomódulo

foriguala__main__paraexecutarmosnoconsolenoPyCharm.Instancieessasclasses,atualize-as

evejaoresultado:

if__name__=='__main__'c=Conta('123-4','Joao',1000.0)cc=ContaCorrente('123-5','Jose',1000.0)cp=ContaPoupanca('123-6','Maria',1000.0)

c.atualiza(0.01)cc.atualiza(0.01)cp.atualiza(0.01)

print(c.saldo)print(cc.saldo)print(cp.saldo)

4. Implementeométodo__str__()naclasseConta.Façacomqueeleimprimaumarepresentação

11.7EXERCÍCIO:HERANÇAEPOLIMORFISMO 151

Page 159: Python e Orientação a Objetos

maisamigáveldeumContacontendotodososseusatributos.

def__str__(self):return"DadosdaConta:\nNumero:{}\nTitular:{}\nSaldo:{}\nLimite:{}".format(self._numero,self._titular,self._limite,self._saldo)

Testechamandoométodoprint(cc).

5. Vamoscriarumaclassequesejaresponsávelporfazeraatualizaçãodetodasascontasbancáriasegerarumrelatóriocomosaldoanterioresaldonovodecadaumadascontas.Napastasrc,criea

classeAtualizadorDeContas:

```pythonclassAtualizadorDeContas:

def__init__(self,selic,saldo_total=0):self._selic=selicself._saldo_total=saldo_total

#propriedades

defroda(self,conta):print("SaldodaConta:{}".format(conta.saldo))self._saldo_total+=conta.atualiza(self._selic)print("SaldoFinal:{}".format(self._saldo_total))

Dica:Paraovalorde_saldo_totalseratualizadocorretamente,atualizetodasasfunçõesatualiza

nasoutrasclassespararetornaremovalorcorretocomreturnself._saldo

Nãoesqueçadefazeros_imports_necessáriosparaocódigofuncionar!

1. Na'main',vamoscriaralgumascontaserodá-lasapartirdoAtualizadorDeContas:

if__name__=='__main__':c=Conta('123-4','Joao',1000.0)cc=ContaCorrente('123-5','José',1000.0)cp=ContaPoupanca('123-6','Maria',1000.0)

adc=AtualizadorDeContas(0.01)

adc.roda(c)adc.roda(cc)adc.roda(cp)

print('Saldototal:{}'.format(adc.saldo_total))

2. (opcional)SevocêprecisassecriarumaclasseContaInvestimento,eseumétodoatualiza()

fossecomplicadíssimo,vocêprecisariaalteraraclasseAtualizadorDeContas?

3. (opcional,Trabalhoso)CrieumaclasseBancoquepossuiumalistadecontas.Reparequeemuma

listadecontasvocêpodecolocartantoContaCorrentequantoContaPoupanca.Crieummétodo

adiciona()queadicionaumacontanalistadecontas;ummétodopegaConta()quedevolvea

contaemdeterminadaposiçãodalistaeoutropegaTotalDeContas()queretornaototaldecontas

na lista.Depois teste criandodiversas contas, insira-asnoBanco e depois, comum laçofor,

152 11.7EXERCÍCIO:HERANÇAEPOLIMORFISMO

Page 160: Python e Orientação a Objetos

percorra todas as contas doBanco para passá-las como argumento para ométodoroda() do

AtualizadorDeContas.

4. (opcional) Que maneira poderíamos implementar o método atualiza() nas classes

ContaCorrenteeContaPoupançapoupandoreescritadecódigo?

5. (opcional)E se criarmosumaclassequenãoé filhadeConta e tentar passar uma instância no

métodorodadeAtualizadorDeContas?Comoqueaprendemosatéaqui, comopodemosevitar

queerrosaconteçamnestescasos?

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

VamosrecordarnossaclasseFuncionario:

classFuncionario:

def__init__(self,nome,cpf,salario=0):#inicializaçãodosatributos

#propriedadeseoutrosmétodos

defget_bonificacao(self):returnself._salario*1.2

ConsidereagoranossoControleDeBonificacao:

classControleDeBonificacoes:

def__init__(self,total_bonificacoes=0):self.__total_bonificacoes=total_bonificacoes

defregistra(self,obj):if(hasattr(obj,'get_bonificacao')):self.__total_bonificacoes+=obj.get_bonificacao()else:

Seuslivrosdetecnologiaparecemdoséculopassado?

11.8CLASSESABSTRATAS

11.8CLASSESABSTRATAS 153

Page 161: Python e Orientação a Objetos

print('instânciade{}nãoimplementaométodoget_bonificacao()'.format(self.__class__.__name__))

#propriedades

Nossométodoregistra() recebeumobjetodequalquer tipo.Todavia, estamos esperandoque

seja um Funcionario já que este implementa ométodo get_bonificacao(), isto é, podem ser

objetos do tipo Funcionario e qualquer de seus subtipos. Dessa forma, qualquer subclasse que

eventualmente venha ser escrita, sem prévio conhecimento do autor da ControleDeBonificacao

podemserimplementadas.

EstamosutilizandoaquiaclasseFuncionarioparaopolimorfismo.Senãofosseporela,teríamos

umgrandeprejuízo:precisaríamoscriarummétodoregistra() para receber cadaumdos tiposde

Funcionario,umparaGerente,umparaDiretor,etc.Reparequeperderessepoderémuitopior

doqueapequenavantagemqueaherançatrazemherdarcódigo.

Todavia,emalgunssistemas,comoéonossocaso,usamosumaclassecomapenasessesintuitos:deeconomizar um pouco código e ganhar polimorfismo para criar métodos mais genéricos, que seencaixemadiversosobjetos.

Faz sentido ter um objeto do tipo Funcionario? Esta pergunta é bastante relevante, já que

instanciar um Funcionario pode gerar um objeto que não faz sentido no nosso sistema. Nossa

empresatemapenasDiretores,Gerentes,Secretários,etc...Funcionarioéapenasumaclasse

queidealizaumtipo,defineapenasumrascunho.

Vejamosumoutrocasoemquenãofazsentidoterumobjetodedeterminadotipo,apesardaclasseexistir.ImagineaclassePessoaeduasfilhas:PessoaFisicaePessoaJuridica.Quandopuxamos

umrelatóriodenossosclientes(umalistadeobjetosdetipoPessoa,porexemplo),queremosquecada

umdelessejaouumaPessoaFisicaouumaPessoaJuridica.AclassePessoa,nessecaso,estaria

sendo usada apenas para ganhar o polimorfismo e herdar algumas coisas - não faz sentido permitirinstanciá-la.

Paraonossosistema,éinadmissívelqueumobjetosejaapenasdotipoFuncionario(podeexistir

umsistemaemquefaçasentidoterobjetosdotipoFuncionarioouapenasPessoa,mas,nonosso

caso,não).Pararesolveressesproblemas,temosasclassesabstratas.

UtilizaremosumamódulodoPythonchamadoabcquepermitedefinirmosclassesabstratas.UmaclasseabstratadeveherdardeABC(AbstractBaseClasses).ABCéasuperclasseparaclassesabstratas.

Umaclasseabstratanãopodeserinstanciadaedeveconterpelomenosummétodoabstrato.Vamosverissonaprática.

VamostornarnossaclasseFuncionarioabstrata:

154 11.8CLASSESABSTRATAS

Page 162: Python e Orientação a Objetos

importabc

classFuncionario(abc.ABC):

#métodosepropriedades

Definimos nossa classe Funcionario como abstrata. Agora vamos tornar nosso método

get_bonificacao()abstrato.Ummétodoabstratopodeter implementação,masnãofazsentidoem

nosso sistema, portanto vamos deixá-lo sem implementação. Para definirmos um método abstrato,utilizamosodecorador@abstractmethod:

classFuncionario(abc.ABC):

@abc.abstractmethoddefget_bonificacao(self):pass

Agora,setentarmosinstanciarumobjetodotipoFuncionario:

if__name__=='__main__':f=Funcionario()

Acusaumerro:

TypeError:Can'tinstantiateabstractclassFuncionariowithabstractmethodsget_bonificacao

ApesardenãoconseguirinstanciaraclasseFuncionario,conseguimosinstanciarsuasfilhasque

sãoobjetosquerealmenteexistememnossosistema(objetosconcretos):

classGerente(Funcionario):#outrosmétodosepropriedades

defget_bonificacao(self):returnself._salario*0.15

if__name__=='__main__':gerente=Gerente('jose','222222222-22',5000.0,'1234',0)print(gerente.get_bonificacao())

VamoscriaraclasseDiretorqueherdadeFucionariosemométodoget_bonificacao():

classDiretor(Funcionario):def__init__(self,nome,cpf,salario):super().__init__(nome,cpf,salario)

if__name__=='__main__':diretor=Diretor('joao','111111111-11',4000.0)

Quandorodamosocódigo:

TypeError:Can'tinstantiateabstractclassDiretorwithabstractmethodsget_bonificacao

NãoconseguimosinstanciarumasubclassedeFuncionario semimplementarométodoabstrato

get_bonificacao().Agora,tornamosométodoget_bonificacao()obrigatórioparatodoobjetoque é subclasse de Funcionario. Caso venhamos a criar outras classes, como Secretaria e

11.8CLASSESABSTRATAS 155

Page 163: Python e Orientação a Objetos

Presidente , que sejam filhas de Funcionario , seremos obrigados e criar o método

get_bonificacao(),casocontrário,ocódigovaiacursarerroquandoexecutado.

1. TorneaclasseContaabstrata.

importabc

classConta(abc.ABC):

def__init__(self,numero,titular,saldo=0,limite=1000.0):self._numero=numeroself._titular=titularself._saldo=saldoself._limite=limite

#outrosmétodosepropriedades

2. Torneométodoatualiza()abstrato:

classConta(abc.ABC):

#códigoomitido

@abc.abstractmethoddefatualiza():pass

3. TenteinstânciarumaConta:

if__name__=='__main__':c=Conta()

Oqueocorre?

4. Agora, instancie uma ContaCorrente e uma ContaPoupanca, e teste o código chamando o

métodoatualiza().

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)cp=ContaPoupanca('123-5','José',1000.0)

cc.atualiza(0.01)cp.atualiza(0.01)

print(cc.saldo)print(cp.saldo)

5. CrieumaclassechamadaContaInvestimento:

classContaInvestimeto(Conta):pass

6. InstancieumaContaInvestimeto:

11.9EXERCÍCIOS-CLASSESABSTRATAS

156 11.9EXERCÍCIOS-CLASSESABSTRATAS

Page 164: Python e Orientação a Objetos

ci=ContaInvestimento('123-6','Maria',1000.0)

7. Não conseguimos instanciar uma ContaInvestimento que herda Conta sem implementar o

método abstrato atualiza() . Vamos criar uma implementação dentro da classe

ContaInvestimento:

defatualiza(self,taxa):self._saldo+=self._saldo*taxa*5

8. AgoratesteinstanciandoumaContaInvestimentoechameométodoatualiza():

ci=ContaInvestimento('123-6','Maria',1000)ci.deposita(1000.0)ci.atualiza(0.01)print(ci.saldo)

9. (opcional) Crie um atributo tipo nas classes ContaCorrente , ContaPoupanca e

ContaInvestimento. Faça com que o tipo também seja impresso quando usamos a função

print().

11.9EXERCÍCIOS-CLASSESABSTRATAS 157

Page 165: Python e Orientação a Objetos

CAPÍTULO12

ImaginequeumSistemadeControledoBancopodeseracessado,alémdosGerentes,pelosDiretoresdoBanco.TeríamosumaclasseDiretor.

classDiretor(Funcionario):

defautentica(self,senha):#verificaseasenhaconfere

EaclasseGerente:

classGerente(Funcionario):

defautentica(self,senha):#verificaseasenhaconfereetambémseoseudepartamentotemacesso

ReparequeométododeautenticaçãodecadatipodeFuncionariopodevariarmuito.Masvamos

aosproblemas.Considere oSistemaInterno e seu controle: precisamos receber umDiretor ou

Gerentecomoargumento,verificarseeleseautenticaecolocá-lodentrodosistema.

Vimos que podemos utilizar a função hasattr() para verificar se um objeto possui ométodo

autentica():

classSistemaInterno:

deflogin(self,funcionario):if(hasattr(obj,'autentica')):#chamamétodoautenticaelse:#imprimemensagemdeaçãoinválida

Maspodemosesquecer,nofuturo,quandomodelarmosaclassePresidente (que tambéméum

funcionário e autenticável), de implementar ométodoautentica().Não faz sentido colocarmos o

métodoautentica()naclasseFuncionariojáquenemtodofuncionárioéautenticável.

Uma solução mais interessante seria criar uma classe no meio da árvore de herança, aFuncionarioAutenticavel:

classFuncionarioAutenticavel(Funcionario):

defautentica(self,senha):#verificaseasenhaconfere

HERANÇAMÚLTIPLAEINTERFACES

158 12HERANÇAMÚLTIPLAEINTERFACES

Page 166: Python e Orientação a Objetos

EasclassesDiretor,GerenteequalqueroutrotipodeFuncionarioAutenticavelqueviera

existir em nosso sistema bancário passaria a estender de FuncionarioAutenticavel. Repare que

FuncionarioAutenticaveléfortecandidataaclasseabstrata.Maisainda,ométodoautentica()

poderiaserummétodoabstrato.

Ousodeherançasimplesresolveocaso,masvamosaumaoutrasituaçãoumpoucomaiscomplexa:todososclientestambémdevempossuiracessoaoSistemaInterno.Oquefazer?

Umaopçãoéfazerumaherançasemsentidopararesolveroproblema,porexemplo,fazerCliente

estender deFuncionarioAutenticavel. Realmente resolve o problema,mas trará diversos outros.

ClientedefinitivamentenãoéumFuncionarioAutenticavel.Sevocêfizerisso,oClienteterá,

por exemplo, ummétodoget_bonificacao(), um atributo salario e outros membros que não

fazemomenorsentidoparaestaclasse.

Precisamos,pararesolveresteproblema,arranjarumaformadereferenciarDiretor,Gerentee

Clientedeumamesmamaneira,istoé,acharumfatorcomum.

Seexistisseumaformanaqualessasclassesgarantissemaexistênciadeumdeterminadométodo,atravésdeumcontrato,resolveríamosoproblema.Podemoscriarum"contrato"quedefinetudooqueumaclassedevefazersequiserterumdeterminadostatus.Imagine:

contratoAutenticavel

-quemquiserserAutenticavelprecisasaberfazer:autenticardadaumasenha,devolvendoumbooleano

Quem quiser pode assinar este contrato, sendo assim obrigado a explicar como será feita essaautenticação.Avantageméque,seumGerenteassinaressecontrato,podemosnosreferenciaraum

GerentecomoumAutenticavel.

ComoPythonadmiteherançamúltipla,podemoscriaraclasseAutenticavel:

classAutenticavel:

defautentica(self,senha):#verificaseasenhaconfere

EfazerGerente,DiretoreClienteherdaremessaclasse:

classGerente(Funcionario,Autenticavel):#códigoomitido

classDiretor(Funcionario,Autenticavel):#códigoomitido

classCliente(Autenticavel):#códigoomitido

Ouseja,GerenteeDiretoralémdefuncionáriossãoautenticáveis!Assim,podemosutilizaro

12HERANÇAMÚLTIPLAEINTERFACES 159

Page 167: Python e Orientação a Objetos

SistemaInternoparafuncionáriosautenticáveiseclientes:

classSistemaInterno:

deflogin(self,obj):if(hasattr(obj,'autentica')):obj.autentica()returnTrueelse:print('{}nãoéautenticável'.format(self.__class__.__name__))returnFalse

if__name__=='__main__':diretor=Diretor('João','111111111-11',3000.0,'1234')gerente=Gerente('José','222222222-22',5000.0,'1235')cliente=Cliente('Maria','333333333-33','1236')

sistema=SistemaInterno()sistema.login(diretor)sistema.login(gerente)sistema.login(cliente)

Notequeumaclassepodeherdardemuitasoutrasclasses.Masvamosaosproblemasqueissopodegerar.Porexemplo,váriasclassespodempossuiromesmométodo.

O exemplo anterior pode parecer uma boa maneira de representar classes autenticáveis, mas secomeçássemosaestenderesse sistema, logoencontraríamosalgumascomplicações.Emumbancodeverdade, as divisões entre gerentes, diretores e clientes nem sempre são claras. Um Cliente, por

exemplo,podeserumFuncionario,umFuncionariopode teroutras subcategoriascomofixose

temporários.

NoPython,épossívelqueumaclasseherdedeváriasoutrasclasses.Poderíamos,porexemplo,criarumaclasseA, que será superclassedasclassesBeC.Aherançamúltipla não émuitodifícil de

entender se uma classe herda de várias classes que possuem propriedades completamente diferentes,masascoisasficamcomplicadasseduassuperclassesimplementamomesmométodoouatributo.

SeasclassesBeCherdaremaclasseAeaclasseDherdarasclassesBeC,easclassesBe

Ctêmummétodom2(),qualmétodoaclasseDherda?

classA:defm1(self):print('métododeA')

classB(A):defm2(self):print('métododeB')

classC(A):defm2(self):print('métododeC')

12.1PROBLEMADODIAMANTE

160 12.1PROBLEMADODIAMANTE

Page 168: Python e Orientação a Objetos

classD(B,C):pass

Essaambiguidadeéconhecidacomooproblemadodiamante,ouproblemadolosango,ediferenteslinguagensresolvemesseproblemademaneirasdiferentes.OPythonsegueumaordemespecíficaparapercorrerahierarquiadeclasseseessaordeméchamadadeMRO:MethodResolutionOrder(OrdemdeResoluçãodeMétodos).

Toda classe temumatributo__mro__ que retorna uma tupla de referências das superclasses na

ordemMRO-daclasseatualatéaclasseobject.VejamosoMROdaclasseD:

print(D.mro())

Saída:

(<class'__main__.D'>,<class'__main__.B'>,<class'__main__.C'>,<class'__main__.A'>,<class'object'>)

Aordemésempredaesquerdaparadireita.ReparequeoPythonvaiprocurarachamadadométodom2()primeironaclasseD,nãoencontrandovaiprocuraremB(aprimeiraclasseherdada).Casonão

encontreemB,vaiprocuraremCesóentãoprocuraremA-eporúltimonaclasseobject.

Tambémpodemosacessaroatributo__mro__atravésdométodomro()chamadopelaclasseque

retornaumalistaaoinvésdeumatupla:

print(D.mro())

saída:[<class'__main__.D'>,<class'__main__.B'>,<class'__main__.C'>,<class'__main__.A'>,<class'object'>]

Portanto,seguindooMRO,aclasseDchamaométodom2()daclasseB:

12.1PROBLEMADODIAMANTE 161

Page 169: Python e Orientação a Objetos

d=D()d.m1()d.m2()

Saída:

métododeAmétododeB

Felizmente,afunçãosuper()sabecomolidardeformainteligentecomherançamúltipla.Seusá-

ladentrodométodo,todososmétodosdassuperclassesdevemserchamadosseguindooMRO.

classA:defm1(self):print('métododeA')

classB(A):

defm1(self):super().m1()

defm2(self):print('métododeB')

classC(A):defm1(self):super().m1()

defm2(self):print('métododeC')

classD(B,C):defm1(self):super().m1()

defm2(self):super().m2()

if__name__=='__main__':d=D()d.m1()d.m2()

Geraasaída:

métododeAmétododeB

162 12.1PROBLEMADODIAMANTE

Page 170: Python e Orientação a Objetos

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Se usarmos herança múltipla, geralmente é uma boa ideia projetarmos nossas classes de umamaneiraqueeviteotipodeambiguidadedescritaacima-apesardoPythonpossuiroMRO,emsistemasgrandesaherançamúltiplaaindapodecausarmuitosproblemas.

Umamaneiradefazerissoédividirafuncionalidadeopcionalemmix-ins.Ummix-inéumaclassequenãosedestinaaserindependente-existeparaadicionarfuncionalidadeextraaoutraclasseatravésdeherançamúltipla.Aideiaéqueclassesherdemestesmix-ins,essas"misturasdefuncionalidades".

Por exemplo, nossa classe Autenticavel pode ser um mix-in, já que ela existe apenas para

acrescentarafuncionalidadedeserautenticável,ouseja,paraherdarseumétodoautentica.

NossaclasseAutenticaveljásecomportacomoumMix-In.NoPythonnãoexisteumamaneira

específicadecriarmix-ins.Osprogramadores,porconvençãoeparadeixarexplícitoaclassecomoummix-in,colocamotermo'MixIn'nonomedaclasseeutilizamatravésdeherançamúltipla:

classAutenticavelMixIn:defautentica(self,senha):#verificasenha

Cadamix-iné responsávelpor fornecerumapeçaespecíficade funcionalidadeopcional.Podemosteroutrosmix-insnonossosistema:

classAtendimentoMixIn:defcadastra_atendimento(self):#fazcadastroatendimento

defatende_cliente(self):#fazatendimento

classHoraExtraMixIn:

defcalcula_hora_extra(self,horas):#calculahorasextras

Agoraéamelhorhoradeaprenderalgonovo

12.2MIX-INS

12.2MIX-INS 163

Page 171: Python e Orientação a Objetos

Epodemosmisturá-losnasclassesdenossosistema:

classGerente(Funcionario,AutenticavelMixIn,HoraExtraMixIn):pass

classDiretor(Funcionario,AutenticavelMixIn):pass

classCliente(AutentivavelMixIn):pass

classEscriturario(Funcionario,AtentimentoMixIn):pass

Reparequenossosmix-ins não têm ummétodo__init__() .Muitosmix-ins apenas fornecem

métodos adicionais, mas não inicializam nada. Isso às vezes significa que eles dependem de outraspropriedadesquejáexistememsuasfilhas.Cadamix-inéresponsávelporfornecerumapeçaespecíficadefuncionalidadeopcional-éumjeitodecomporclasses.

Poderíamosestenderesteexemplocommaismisturasquerepresentamacapacidadedepagartaxas,acapacidadedeserpagoporserviços,eassimpordiante -poderíamosentãocriarumahierarquiadeclassesrelativamenteplanaparadiferentestiposdeclassesdefuncionárioqueherdamFuncionarioe

algunsmix-ins.

Essaéumadasabordagensdeseusarherançamúltipla,maselaébastantedesencorajada.Casovocêutilize, opte pormix-ins sabendo de suas desvantagens.Usando em sistemas grandes, podemocorrercolisõescomnomesdemétodos,métodossubstituídosacidentalmente,hierarquiadeclassespoucoclarae dificuldade de ler e entender classes compostas pormuitosmix-ins, dentre outras desvantagens. Oproblemadaherançamúltiplapermanece.

Outra abordagem possível é definir funções fora de classes, digamos em um módulo e fazerchamadas dessas funções passando nossos objetos.Mas isso é um afastamento radical do paradigmaorientadoaobjetos,queébaseadaemmétodosdefinidosdentrodasclasses.

TkinteréumframeworkquefazpartedabibliotecapadrãodoPythonutilizadoparacriarinterfacegráfica.Éumcasoondemix-instrabalhambemjáquesetratadeumpequenoframework,mastambémé suficientemente grande para que seja possível ver o problema. Veja um exemplo de parte de suahierarquiadeclasse:

12.3PARASABEMAIS-TKINTER

164 12.3PARASABEMAIS-TKINTER

Page 172: Python e Orientação a Objetos

AimagemacimafoiretiradadolivroPythonFluentedeLucianoRamalho-mostrandopartedo

complicadomodelodeclassesutilizandoherançamúltipladopacoteTkinter.Asetasrepresentamo

MROquedeveiniciarnaclasseText.AclasseTextimplementaumcampodetextoeditáveletem

muitasfuncionalidadespróprias,alémdeherdarmuitosmétodosdeoutrasclasses.

UmaoutraclassedopacotequenãoaparecenestediagramaéaLabel,utilizadaparamostrarum

texto ou bitmap na tela. Você pode testar no Pycharm, aproveitando a ferramenta de autocomplete,chamandoTkinter.Label.eaIDEvai temostrar181sugestõesdeatributosemumaúnicaclasse!

Ouvocêpodeutilizarafunçãohelp()parachecaraorigemdecadaumdeles.

fromtkinterimport*

help(Label)

Essepacotetemmaisde20anoseéumexemplodecomoaherançamúltiplaerautilizadaquandoosprogramadoresnãoconsideravamsuasdesvantagens.Apesardamaioriadasclassessecomportaremcomomix-ins,opadrãodenomenclaturanãoerautilizado.Felizmente,oTkinter é um framework

estável.

1. Nossobancoprecisatributardinheirodealgunsbensquenossosclientespossuem.ParaissovamoscriarumaclasseTributavelcomummétodoquedevolveoimpostosobreaconta:

classTributavel:

defget_valor_imposto(self):pass

Lemosessaclassedaseguintemaneira:"Todosquequiseremsertributáveisprecisamsaberretornarovalordoimposto".Algunsbenssãotributáveiseoutrosnão.Porexemplo:ContaPoupancanãoé

tributável,jáparaContaCorrentevocêprecisapagar1%dacontaeoSeguroDeVida temuma

12.4EXERCÍCIOS-MIX-INS

12.4EXERCÍCIOS-MIX-INS 165

Page 173: Python e Orientação a Objetos

faixafixade50reaismais5%dovalordoseguro.

2. TransformeaclasseTributavelemummix-in:

classTributavelMixIn:

defget_valor_imposto(self)pass

3. Faça a classe ContaCorrente herdar da classe TributavelMixIn e implemente o método

"exigido"peloMixIn:

classContaCorrente(Conta,TributavelMixIn):#códigoomitido

defget_valor_imposto(self):returnself._saldo*0.01

4. CrieaclasseSeguroDeVidaquevaiherdardeTributavelMixIn.Crieseusrespectivosatributos

eimplementeométododoMixIn:

classSeguroDeVida(TributavelMixIn):

def__init__(self,valor,titular,numero_apolice):self._valor=valorself._titular=titularself._numero_apolice=numero_apolice

defget_valor_imposto(self):return50+self._valor*0.05

5. VamoscriaraclasseManipuladorDeTributaveisemumarquivochamadomanipulador.py.Essa

classedeveterummétodochamadocalcula_impostos() que recebeuma listade tributáveis e

retornaototaldeimpostoscobrados:

classManipuladorDeTributaveis:

defcalcula_impostos(self,lista_tributaveis):total=0fortinlista_tributaveis:total+=t.get_valor_imposto()

returntotal

6. Ainda no arquivo manipulador.py , vamos testar o código. Crie alguns objetos de

ContaCorrente e de SeguroDeVida. Em seguida, crie uma lista de tributáveis e insira seus

objetosnela.InstancieumManipuladorDeTributaveisechameométodocalcula_impostos()

passandoalistadetributáveiscriadaeimprimaovalortotaldosimpostos:

if__name__=='__main__':fromcontaimportContaCorrente,SeguroDeVida,TributavelMixIn

cc1=ContaCorrente('123-4','João',1000.0)cc2=ContaCorrente('123-4','José',1000.0)seguro1=SeguroDeVida(100.0,'José','345-77')

166 12.4EXERCÍCIOS-MIX-INS

Page 174: Python e Orientação a Objetos

seguro2=SeguroDeVida(200.0,'Maria','237-98')

lista_tributaveis=[]lista_tributaveis.append(cc1)lista_tributaveis.append(cc2)lista_tributaveis.append(seguro1)lista_tributaveis.append(seguro2)

manipulador=ManipuladorDeTributaveis()total=manipulador.calcula_impostos(lista_tributaveis)

print(total)

Vimosqueherançamúltiplapodeserperigosa,esenossosistemacrescerpodegerarmuitaconfusãoeconflitodenomesdemétodos.Umamaneiramaiseficaznestescasoséusarclassesabstratascomointerfaces,queveremosaseguir.

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

OPythonnãopossuiumapalavra reservada interface.Mesmosemumapalavra reservadaparaamesma,todaclassepossuiumainterface.Interfacessãoosatributospúblicosdefinidos(queemPythonsãotantoatributosquantométodos)emumaclasse-issoincluiosmétodosespeciaiscomo__str__()

e__add__().

Uma interface vista como um conjunto de métodos para desempenhar um papel é o que osprogramadoresdaSmallTalkchamavamdeprotocolo,eestetermofoidisseminadoemcomunidadesdeprogramadoresdelinguagensdinâmicas.Esseprotocolofuncionacomoumcontrato.

Osprotocolossãoindependentesdeherança.Umaclassepodeimplementarváriosprotocolos,comoos mix-ins. Protocolos são interfaces e são definidos apenas por documentação e convenções emlinguagens dinâmicas, por isso são considerados informais.Os protocolos não podem ser verificadosestaticamentepelointerpretador.

EditoraCasadoCódigocomlivrosdeumaformadiferente

12.5INTERFACES

12.5INTERFACES 167

Page 175: Python e Orientação a Objetos

Ométodo__str__(),porexemplo,éesperadoqueretorneumarepresentaçãodoobjetoemforma

destring.Nadaimpededefazermosoutrascoisasdentrodométodocomodeletaralgumconteúdoou

fazer algum cálculo; ao invés de retornarmos apenas a string. Mas há um entendimento prévio

comumdoqueestemétododevefazereestápresentenadocumentaçãodoPython.Esteéumexemploondeo contrato semântico é descrito emummanual.Algumas linguagensde tipagemestática, comoJava, possuem interfaces em sua biblioteca padrão e podem garantir este contrato em tempo decompilação.

A partir do Python 2.6, a definição de interfaces utilizando omóduloABC é uma soluçãomaiselegantedoqueosmix-ins.NossaclasseAutenticavelpodeserumaclasseabstratacomométodo

abstratoautentica():

importabc

classAutenticavel(abc.ABC):

@abc.abstractmethoddefautentica(self,senha):pass

Como se trata de uma interface em uma linguagem de tipagem dinâmica como o Python, a boapráticaédocumentarestaclassegarantindoocontratosemântico:

importabc

classAutenticavel(abc.ABC):"""Classeabstrataquecontémoperaçõesdeumobjetoautenticável.

Assubclassesconcretasdevemsobrescreverométodoautentica"""

@abc.abstractmethoddefautentica(self,senha):"""Métodoabstratoquefazverificaçãodasenha.DevolveTrueseasenhaconfere,eFalsecasocontrário."""

EnossasclassesGerente,DiretoreClienteherdariamaclasseAutenticavel.Masquala

diferençadeherdarmuitosmix-insemuitasABCs?Realmente,aquinãohágrandediferençaevoltamosaoproblemaanteriordosmix-ins-muitoacoplamentoentreclassesquegeraaherançamúltipla.

MasanovidadedasABCséseumétodoregister().AsABCsintroduzemumasubclassevirtual,

quesãoclassesquenãoherdamdeumaclassemassãoreconhecidaspelosmétodosisinstance()e

issubclass() . Ou seja, nosso Gerente não precisa herdar a classe Autenticavel , basta

registrarmoselecomoumaimplementaçãodaclasseAutenticavel.

Autenticavel.register(Gerente)

Etestamososmétodosisinstance()eissubclass()comumainstânciadeGerente:

gerente=Gerente('João','111111111-11',3000.0)

168 12.5INTERFACES

Page 176: Python e Orientação a Objetos

print(isinstance(Autenticavel))print(issubclass(Autenticavel))

Quevaigerarasaída:

TrueTrue

OPython não vai verificar se existe uma implementação dométodoautentica em Gerente

quando registrarmos a classe. Ao registrarmos a classe Gerente como uma Autenticavel ,

prometemosaoPythonqueaclasseimplementafielmenteanossainterfaceAutenticaveldefinida.O

PythonvaiacreditarnissoeretornarTruequandoosmétodosisinstance()eissubclass()foremchamados.Sementirmos,ouseja,nãoimplementarmosométodoautentica()emGerente, uma

exceçãoserálançadaquandotentarmoschamarestemétodo.

VejamosumexemplocomaclasseDiretor.Nãovamosimplementarométodoautentica()e

registrarumainstânciadeDiretorcomoumAutenticavel:

classDiretor(Funcionario):#códigoomitido

if__name__=='__main__':Autenticavel.register(Diretor)

d=Diretor('José','22222222-22',3000.0)

d.autentica('?')

Etemoscomosaída:

Traceback(mostrecentcalllast):File<stdin>,line47,in<module>d.autentica('?')AttributeError:'Diretor'objecthasnoattribute'autentica'

Novamente,podemostrataraexceçãoouutilizarosmétodosisinstance() ouissubclass()

para verificação. Apesar de considerada má práticas por muitos pythonistas, o módulo de classesabstratas justifica a utilização deste tipo de verificação. A verificação não é de tipagem,mas se umobjetoestádeacordocomainterface:

if__name__=='__main__':Autenticavel.register(Diretor)

d=Diretor('José','22222222-22',3000.0)

if(isinstance(d,Autenticavel)):d.autentica('?')else:print("DiretornãoimplementaainterfaceAutenticavel")

Eportanto,nossaclasseSistemaInternoficariaassim:

fromautenticavelimportAutenticavel

12.5INTERFACES 169

Page 177: Python e Orientação a Objetos

classSistemaInterno:

deflogin(self,obj):if(isinstance(obj,Autenticavel)):obj.autentica(obj.senha)returnTrueelse:print("{}nãoéautenticável".format(self.__class__.__name__))returnFalse

Dessa maneira fugimos da herança múltipla e garantimos um contrato, um protocolo. Classesabstratas complementam oduck typing, provendo umamaneira de definir interfaces quando técnicascomousarhasattr()sãoruinsousutilmenteerradas.Vocêpodelermaisarespeitonodocumentoda

PEP que introduz classes abstratas - é a PEP 3119 e você pode acessar seu conteúdo neste link:https://www.python.org/dev/peps/pep-3119/

AlgumasABCs tambémpodemprovermétodosconcretos,ou seja,nãoabstratos.Por exemplo, aclasse Interator do módulo collections da biblioteca padrão do Python possui um método

__iter__()retornandoelemesmo.EstaABCpodeserconsideradaumaclassemix-in.

OPythonjávemcomalgumasestruturasabstratas(vermóduloscollections,numberseio).

1. Nossobancoprecisatributardinheirodealgunsbensquenossosclientespossuem.Paraisso,vamoscriar uma classe Tributavel no módulo tributavel.py, e adicionar um método que devolve o

impostosobreaconta:

classTributavel:

defget_valor_imposto(self):pass

Lemosessaclassedaseguintemaneira:"todosquequiseremsertributáveisprecisamsaberretornarovalordoimposto".Algunsbenssãotributáveiseoutrosnão,ContaPoupancanãoétributável,já

paraContaCorrentevocêprecisapagar1%dacontaeoSeguroDeVidatemumafaixafixade50

reaismais5%dovalordoseguro.

2. TorneaclasseTributavelumaclasseabstrata:

importabc

classTributavel(abc.ABC):

defget_valor_imposto(self)pass

3. Ométodoget_valor_imposto()tambémdeveserabstrato:

importabc

12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS

170 12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS

Page 178: Python e Orientação a Objetos

classTributavel(abc.ABC):

@abc.abstractmethoddefget_valor_imposto(self,valor):pass

4. Nada impede que os usuários de nossa classe tributavel implementem o métodoget_valor_impostodemaneiranãoesperadapornós.Então,vamosacrescentaradocumentação

utilizandodocstringqueaprendemosnocapítulodemódulos:

importabc

classTributavel(abc.ABC):"""Classequecontémoperaçõesdeumobjetoautenticável

Assubclassesconcretasdevemsobrescreverométodoget_valor_imposto."""@abc.abstractmethoddefget_valor_imposto(self):"""aplicataxadeimpostosobreumdeterminadovalordoobjeto"""pass

5. Utilizaafunçãohelp()passandoaclasseTributavelparaacessaradocumentação.

6. FaçaaclasseContaCorrenteherdardaclasseTributavel:

classContaCorrente(Conta,Tributavel):#códigoomitido

defget_valor_imposto(self):returnself._saldo*0.01

7. Crie a classe SeguroDeVida, com os atributos valor, titular e numero_apolice, que

tambémdeve ser um tributável. Implementeométodoget_valor_imposto() de acordo coma

regradenegóciodefinidapeloexercício:

classSeguroDeVida(Tributavel):

def__init__(self,valor,titular,numero_apolice):self._valor=valorself._titular=titularself._numero_apolice=numero_apolice

defget_valor_imposto(self):return50+self._valor*0.05

8. Vamos criar a classe ManipuladorDeTributaveis em um arquivo chamado

manipulador_tributaveis.py.Essaclassedeveterummétodochamadocalcula_impostos() que

recebeumaslistadetributáveiseretornaototaldeimpostoscobrados:

classManipuladorDeTributaveis:

defcalcula_impostos(self,lista_tributaveis):total=0fortinlista_tributaveis:total+=t.get_valor_imposto()

12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS 171

Page 179: Python e Orientação a Objetos

returntotal

9. Nossas classes ContaCorrente e SeguroDeVida já implementam o método

get_valor_imposto().Vamosinstanciarcadaumasdelasetestarachamadadométodo:

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)seguro=SeguroDeVida(100.0,'José','345-77')

print(cc.get_valor_imposto())print(seguro.get_valor_imposto())

10. Crieuma listacomosobjetoscriadosnoexercícioanterior, instancieumobjetodo tipolist e

passealistachamandoométodocalcula_impostos().

if__name__=='__main__':

#códigoomitido

lista_tributaveis=[]lista_tributaveis.append(cc)lista_tributaveis.append(seguro)

mt=ManipuladorDeTributaveis()total=mt.calcula_impostos(lista_tributaveis)print(total)

11. Nosso código funciona, mas ainda estamos utilizando herança múltipla! Vamos melhorar nossocódigo.FaçacomqueContaCorrenteeSeguroDeVidanãomaisherdemdaclasseTributavel:

classContaCorrente(Conta):#códigoomitido

classSeguroDeVida:#códigoomitido

Vamos registrar nossas classes ContaCorrente e SeguroDeVida como subclasses virtuais de

Tributavel,demodoquefuncionecomoumainterface.

if__name__=='__main__':fromtributavelimportTributavel

cc=ContaCorrente('João','123-4')cc.deposita(1000.0)

seguro=SeguroDeVida(100.0,'José','345-77')

Tributavel.register(ContaCorrente)Tributavel.register(SeguroDeVida)

lista_tributaveis=[]lista_tributaveis.append(cc)lista_tributaveis.append(seguro)

mt=ManipuladorDeTributaveis()total=mt.calcula_impostos(lista_tributaveis)print(total)

172 12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS

Page 180: Python e Orientação a Objetos

12. Modifiqueométodocalcula_impostos()daclasseManipuladorDeTributaveisparachecarse

oselementosdalistassãotributáveisatravésdométodoisinstance().Casoumobjetoda lista

não seja um tributável, vamos imprimir uma mensagem de erro e apenas os tributáveis serãosomadosaototal:

classManipuladorDeTributaveis:

defcalcula_impostos(self,lista_tributaveis):total=0fortinlista_tributaveis:if(isinstance(t,Tributavel)):total+=t.get_valor_imposto()else:print(t.__repr__(),"nãoéumtributável")returntotal

Testenovamentecomalistadetributáveisquefizemosnoexercícioanteriorevejasetudocontinuafuncionando.

13. ContaPoupanca nãoéum tributável.Experimente instanciarumaContaPoupanca, adicionar a

listadetributáveisecalcularototaldeimpostosatravésdoManipuladorDeTributaveis:

if__name__=='__main__':#códigoomitidodoexercícioanterioromitido

cp=ContaPoupanca('123-6','Maria')lista_tributaveis.append(cp)

total=mt.calcula_impostos(lista_tributaveis)print(total)

Oqueacontece?

14. (Opcional) Agora, além de ContaCorrente e SeguroDeVida , nossa ContaInvestimento

tambémdeveserumtributável,cobrando3%dosaldo.InstancieumaContaInvestimento.

classContaInvestimento(Conta):

defatualiza(self,taxa):self._saldo+=self._saldo*taxa*5

defget_valor_imposto(self):returnself._saldo*0.03

RegistreaclasseContaInvestimentocomotributável.AdicioneaContaInvestimentocriadana

lista de tributáveis do exercício anterior e calcule o total de impostos através doManipuladorDeTributaveis.

if__name__=="__main__":

#códigoomitido

ci=ContaInvestimento('Ana','123-0')ci.deposita(100.0)Tributavel.register(ContaInvestimento)

12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS 173

Page 181: Python e Orientação a Objetos

lista_tributaveis.append(ci)mt=ManipuladorDeTributaveis()

total=mt.calcula_impostos(lista_tributaveis)print(total)

Nestecapítulo,aprendemossobreherançamúltiplaesuasdesvantagensmesmoutilizandomix-ins.Alémdisso,aprendemosautilizarclassesabstratascomointerfacesregistrandoasclasseseevitandoosproblemas com a herança múltipla. Agora nossa classe abstrata Tributavel funciona como um

protocolo. No capítulo sobre omódulo collections, veremos na prática alguns conceitos vistos nestecapítulo.

174 12.6(OPCIONAL)EXERCÍCIOS-INTERFACESECLASSESABSTRATAS

Page 182: Python e Orientação a Objetos

CAPÍTULO13

Voltando às contas que criamos no capítulo 8, o que aconteceria ao tentarmos chamar o métodosaca()comumvalorforadolimite?Osistemamostrariaumamensagemdeerro,masquemchamou

ométodosaca()nãosaberáqueissoaconteceu.

Comoavisaràquelequechamouométododequeelenãoconseguiufazeraquiloquedeveria?

Osmétodosdizemqualocontratoqueelesdevemseguir.Se,aotentarchamarométodosacar(),

elenãoconseguefazeroquedeveria,eleprecisa,aomenos,avisaraousuárioqueosaquenãofoifeito.

Vejanoexemploabaixo:estamosforçandoumaContaaterumvalornegativo,istoé,aestarem

umestadoinconsistentedeacordocomanossamodelagem.

conta=Conta('123-4','João')conta.deposita(100.0)conta.saca(3000.0)

#ométodosacafuncionou?

Em sistemas de verdade, é muito comum que quem saiba tratar o erro é aquele que chamou ométodo,enãoaprópriaclasse!Portanto,nadamaisnaturalsinalizarqueumerroocorreu.

AsoluçãomaissimplesutilizadaantigamenteéademarcaroretornodeummétodocomobooleaneretornarTruesetudoocorreudamaneiraplanejada,ouFalse,casocontrário:

if(valor>self.saldo+self.limite):print("naopossosacarforadolimite")returnFalseelse:self.saldo-=valorreturnTrue

Umnovoexemplodechamadadométodoacima:

conta=Conta('123-4','João')conta.deposita(100.0)conta.limite=100.0

if(notconta.saca(3000.0)):print("naosaquei")

Reparequetivemosdelembrardetestaroretornodométodo,masnãosomosobrigadosafazerisso.Esquecerdetestaroretornodessemétodoteriaconsequênciasdrásticas:amáquinadeautoatendimento

EXCEÇÕESEERROS

13EXCEÇÕESEERROS 175

Page 183: Python e Orientação a Objetos

poderiaviraliberaraquantiadesejadadedinheiro,mesmoseosistemanãotivesseconseguidoefetuarométodosaca()comsucesso,comonoexemploaseguir:

conta=Conta("123-4","João")conta.deposita(100.0)

#...

valor=5000.0conta.saca(valor)#vairetornarFalse,masninguémverifica

caixa_eletronico.emite(valor)

Mesmo invocando o método e tratando o retorno de maneira correta, o que faríamos se fossenecessáriosinalizarquandoousuáriopassouumvalornegativocomovalor?Umasoluçãoseriaalteraroretornodebooleanparaint e retornarocódigodoerroqueocorreu. Issoéconsideradoumamá

prática(conhecidatambémcomousode"magicnumbers").

Alémdevocêperderoretornodométodo,ovalordevolvidoé"mágico"esólegívelperanteextensadocumentação,alémdenãoobrigaroprogramadoratrataresseretornoe,nocasodeesquecerisso,seuprogramacontinuarárodandojánumestadoinconsistente.

Por esses e outros motivos, utilizamos um código diferente para tratar aquilo que chamamos deexceções:oscasosondeacontecealgoque,normalmente,nãoiriaacontecer.Oexemplodoargumentodosaqueinválidooudoidinválidodeumclienteéumaexceçãoàregra.

Umaexceçãorepresentaumasituaçãoquenormalmentenãoocorreerepresentaalgodeestranhoouinesperadonosistema.

Antes de resolvermos o nosso problema, vamos ver como o interpretador age ao se deparar comsituaçõesinesperadas,comodivisãoporzeroouacessoaumíndicedeumalistaquenãoexiste.

Paraaprendermososconceitosbásicosdasexceptions doPython, crieumarquivo teste_erro.py etesteoseguintecódigovocêmesmo:

fromcontaimportContaCorrente

defmetodo1():print('iníciodometodo1')metodo2()print('fimdometodo1')

defmetodo2():print('iníciodometodo2')cc=ContaCorrente('José','123')foriinrange(1,15):cc.deposita(i+1000)print(cc.saldo)if(i==5):cc=None

print('fimdometodo2')

176 13EXCEÇÕESEERROS

Page 184: Python e Orientação a Objetos

if__name__=='__main__':print('iníciodomain')metodo1()print('fimdomain')

Reparequeduranteaexecuçãodoprogramachamamosometodo1()eesse,porsuavez,chamao

metodo2().Cadaumdessesmétodospodetersuasprópriasvariáveislocais,istoé:ometodo1()não

enxergaasvariáveisdeclaradasdentrodoexecutáveleporaíemdiante.

ComooPython(emuitasoutraslinguagens)fazisso?Todainvocaçãodemétodoéempilhadoemumaestruturadedadosqueisolaaáreaememóriadecadaum.Quandoummétodotermina(retorna),elevoltaparaométodoqueo invocou.Eledescobre issoatravésdapilhadeexecução (stack): bastaremoveromarcadorqueestánotopodapilha:

Porém, o nosso metodo2() propositalmente possui um enorme problema: está acessando uma

referênciaparaNonequandooíndiceforiguala6!

Rodeocódigo.Qualasaída?Oqueissorepresenta?Oqueelaindica?

Essasaídaéorastrodepilha,oTraceback.Éumasaídaimportantíssimaparaoprogramador-tantoque,emqualquerfórumoulistadediscussão,écomumosprogramadoresenviarem,juntamentecomadescriçãodoproblema,essaTraceback.Masporqueissoaconteceu?

13EXCEÇÕESEERROS 177

Page 185: Python e Orientação a Objetos

O sistema de exceções do Python funciona da seguintemaneira: quando uma exceção é lançada(raise),ointerpretadorentraemestadodealertaevaiverseométodoatualtomaalgumaprecauçãoaotentarexecutaresse trechodecódigo.Comopodemosver,ometodo2() não tomanenhumamedida

diferentedoquevimosatéagora.

Como o metodo2() não está tratando esse problema, o interpretador para a execução dele

anormalmente, sem esperar ele terminar, e volta um stackframe para baixo, onde será feita novaverificação:"ométodo1() está se precavendodeumproblema chamadoAttributeError?"Se a

respostaénão,elevoltaparaoexecutável,ondetambémnãoháproteção,eointerpretadormorre.

Obviamente,aquiestamosforçandoessecasoenãofariasentidotomarmoscuidadocomele.Éfácilarrumar um problema desses: basta verificar antes de chamar os métodos se a variável está comreferênciaparaNone.

Porém,apenasparaentenderocontroledefluxodeumaException,vamoscolocarocódigoquevaitentar (try) executar um bloco perigoso e, caso o problema seja do tipo AttributeError, ele será

excluído(except).ReparequeéinteressantequecadaexceçãonoPythontenhaumtipo,afinalelapodeteratributosemétodos.

Adicioneumtry/except emvoltadofor, 'pegando' umAttributeError.Oqueo código

imprime?

fromcontaimportContaCorrente

defmetodo1():print('iníciodometodo1')metodo2()print('fimdometodo1')

defmetodo2():print('iníciodometodo2')cc=ContaCorrente('José','123')try:foriinrange(1,15):cc.deposita(i+1000)print(cc.saldo)if(i==5):cc=Noneexcept:print('erro')

print('fimdometodo2')

if__name__=='__main__':print('iníciodomain')metodo1()print('fimdomain')

178 13EXCEÇÕESEERROS

Page 186: Python e Orientação a Objetos

Aoinvésdefazerotryemtornodoforinteiro,tenteapenascomoblocodentrodofor:

defmetodo2():print('iníciodometodo2')cc=ContaCorrente('José','123')

foriinrange(1,15):try:cc.deposita(i+1000)print(cc.saldo)if(i==5):cc=Noneexcept:print('erro')

print('fimdometodo2')

Qualadiferença?

Retireotry/exceptecoloqueeleemvoltadachamadadometodo2():

defmetodo1():print('iníciodometodo1')try:metodo2()exceptAttributeError:print('erro')print('fimdometodo1')

13EXCEÇÕESEERROS 179

Page 187: Python e Orientação a Objetos

Faça o mesmo, retirando o try/except novamente e colocando-o em volta da chamada do

metodo1().Rodeoscódigos,oqueacontece?

if__name__=='__main__':print('iníciodomain')try:metodo1()exceptAttributeError:print('erro')print('fimdomain')

Repareque,apartirdomomentoqueumaexceptionfoicatched(pega,tratada,handled),aexceçãovoltaaonormalapartirdaqueleponto.

Runtime

Estetipodeerroocorrequandoalgodeerradoaconteceduranteaexecuçãodoprograma.Amaiorpartedasmensagensdestetipodeerroincluiinformaçõesdoqueoprogramaestavafazendoeolocalqueoerroaconteceu.

OinterpretadormostraafamosaTraceback-elemostraasequênciadechamadasdefunçãoquefezcom que você chegasse onde está, incluindo o número da linha de seu arquivo onde cada chamadaocorreu.

Oserrosmaiscomunsdetempodeexecuçãosão:

13.1EXCEÇÕESETIPOSDEERROS

180 13.1EXCEÇÕESETIPOSDEERROS

Page 188: Python e Orientação a Objetos

NameError

Quandotentamosacessarumavariávelquenãoexiste.

print(x)

Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>NameError:name'x'isnotdefined

Noexemploacima,tentamosimprimirxsemdefini-loantes.Esteerrotambémémuitocomumde

ocorrerquandotentamosacessarumavariávellocalemumcontextoglobal.

TypeError

Quando tentamos usar um valor de forma inadequada, como por exemplo tentar indexar umsequênciacomalgodiferentedeumnúmerointeirooudeumfatiamento:

lista=[1,2,3]print(lista['a'])

Traceback(mostrecentcalllast):File"<stdin>",line2,in<module>TypeError:listindicesmustbeintegersorslices,notstr

KeyError

Quandotentamosacessarumelementodeumdicionáriousandoumachavequenãoexiste.

dicionario={'nome':'João','idade':25}print(dicionario['cidade'])

Traceback(mostrecentcalllast):File"<stdin>",line2,in<module>KeyError:'cidade'

AttributeError

Quandotentamosacessarumatributooumétodoquenãoexisteemumobjeto.

lista=[1,2,3]print(lista.nome)

Traceback(mostrecentcalllast):File"<stdin>",line2,in<module>AttributeError:'list'objecthasnoattribute'nome'

IndexError

Quandotentamosacessarumelementodeumasequênciacomumíndicemaiorqueototaldeseuselementosmenosum.

tupla=(1,2,3)print(tupla[3])

Traceback(mostrecentcalllast):

13.1EXCEÇÕESETIPOSDEERROS 181

Page 189: Python e Orientação a Objetos

File"<stdin>",line2,in<module>IndexError:tupleindexoutofrange

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

Hámuitos outros erros de tempo de execução.Que tal dividir um número por zero? Será que ointerpretadorconseguefazeraquiloquenósdefinimosquenãoexiste?

n=2n=n/0

Traceback(mostrecentcalllast):File"<stdin>",line2,in<module>ZeroDivisionError:divisionbyzero

ReparequeumZeroDivisionErrorpoderiaserfacilmenteevitadocomumifquechecariaseo

denominador é diferente de zero, mas a forma correta de se tratar um erro no Python é através docomandotry/except:

try:n=n/0exceptZeroDivisionError:print('divisãoporzero')

Quegeraasaída:

divisãoporzero

Oconjuntode instruçõesdentrodoblocotry é executado (o interpretador tentará executar).Se

nenhumaexceçãoocorrer,ocomandoexceptéignoradoeaexecuçãoéfinalizada.Todavia,seocorrer

algumaexceçãoduranteaexecuçãodoblocotry,asinstruçõesremanescentesserãoignoradas,esea

exceçãolançadapreverumexcept,asinstruçõesdentrodoblocoexceptserãoexecutadas.

Ocomandotrypodetermaisdeumcomandoexceptparaespecificarmúltiplostratadorespara

JáconheceoscursosonlineAlura?

13.2TRATANDOEXCEÇÕES

182 13.2TRATANDOEXCEÇÕES

Page 190: Python e Orientação a Objetos

diferentesexceções.Nomáximoumúnicotratadorseráativado.Tratadoressósãosensíveisàsexceçõeslevantadasnointeriordacláusulatry,enãoasquetenhamocorridonointeriordeoutrotratadornuma

mesmainstruçãotry.Umtratadorpodesersensívelamúltiplasexceções,desdequeasespecifiqueem

umatupla:

except(RuntimeError,TypeError,NameError):pass

Aúltimacláusulaexceptpodeomitironomedaexceção,funcionandocomoumcoringa.Nãoé

aconselhávelabusardesterecurso,jáqueissopodeescondererrosdoprogramadoredousuário.

Oblocotry/exceptpossuiumcomandoopcionalelseque,quandousado,devesercolocado

depoisdetodososcomandosexcept.Éumcomandoútilparacódigosqueprecisamserexecutadosse

nenhumaexceçãofoilançada,porexemplo:

try:arquivo=open('palavras.txt','r')exceptIOError:print('nãofoipossívelabriroarquivo')else:print('oarquivotem{}palavras'.format(len(arquivo.readlines())))arquivo.close()

O comando raise nos permite forçar a ocorrência de um determinado tipo de exceção. Por

exemplo:

raiseNameError('oi')Traceback(mostrecentcalllast):File"<stdin>",line1,in?NameError:oi

Oargumentoderaiseindicaaexceçãoaserlançada.Esseargumentodeveserumainstânciade

Exceptionouumaclassedealgumaexceção-umaclassequederivadeException.

Casovocêprecisedeterminarseumaexceçãofoi lançadaounão,masnãoquermanipularoerro,umaformaélançá-lanovamenteatravésdainstruçãoraise:

try:raiseNameError('oi')exceptNameError:print('lançouumaexceção')raise

Saída:

lançouumaexceçãoTraceback(mostrecentcalllast):File"<stdin>",line1,in?NameError:oi

13.3LEVANTANDOEXCEÇÕES

13.3LEVANTANDOEXCEÇÕES 183

Page 191: Python e Orientação a Objetos

Programaspodemdefinirnovostiposdeexceções,atravésdacriaçãodeumanovaclasse.ExceçõesdevemserderivadasdaclasseException,diretaouindiretamente.Porexemplo:

classMeuErro(Exception):def__init__(self,valor):self.valor=valordef__str__(self):returnrepr(self.valor)

if__name__=='__main__':try:raiseMeuErro(2*2)exceptMeuErroase:print('Minhaexceçãoocorreu,valor:{}'.format(e.valor))

raiseMeuErro('oops!')

Quequandoexecutadogeraasaída:

Minhaexceçãoocorreu,valor:4Traceback(mostrecentcalllast):File"<stdin>",line13,in<module>raiseMeuErro('oops!')__main__.MeuErro:'oops!'

Nesteexemplo,ométodo__init__daclasseException foi reescrito.Onovocomportamento

simplesmentecriaoatributovalor.Classesdeexceçõespodemserdefinidasparafazerqualquercoisaquequalqueroutraclassefaz,masemgeralsãobemsimples,frequentementeoferecendoapenasalgunsatributosqueforneceminformaçõessobreoerroqueocorreu.

Aocriarummóduloquepodegerardiversoserros,umapráticacomumécriarumaclassebaseparaas exceções definidas por aquelemódulo, e as classes específicas para cada condição de erro comosubclassesdela:

classMeuErro(Exception):"""Classebaseparaoutrasexceções"""pass

classValorMuitoPequenoError(MeuErro):"""Élançadaquandoovalorpassadoémuitopequeno"""pass

classValorMuitoGrandeError(MeuErro):"""Élançadaquandoovalorpassadoémuitogrande"""pass

EssaéamaneirapadrãodedefinirexceçõesnoPython,masoprogramadornãoprecisaficarpresoaela.É comumquenovas exceções sejamdefinidas comnomes terminando em“Error”, semelhante amuitasexceçõesembutidas.

13.4DEFINIRUMAEXCEÇÃO

184 13.4DEFINIRUMAEXCEÇÃO

Page 192: Python e Orientação a Objetos

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

O comando try pode ter outro comando opcional, chamado finally. Sua finalidade é permitir aimplementação de ações de limpeza, que sempre devem ser executadas independentemente daocorrênciadeexceções.Comonoexemplo:

defdivisao(x,y):try:resultado=x/yexceptZeroDivisionError:print("Divisãoporzero")else:print("oresultadoé{}".format(resultado))finally:print("executandoofinally")

if__name__=='__main__':divide(2,1)divide(2,0)divide('2','1')

Executando:

resultadoé2executandoofinallydivisãoporzeroexecutandoofinallyexecutandoofinallyTraceback(mostrecentcalllast):File"<stdin>",line1,in?File"<stdin>",line3,individeTypeError:unsupportedoperandtype(s)for/:'str'and'str'

Reparequeoblocofinallyéexecutadoemtodososcasos.AexceçãoTypeError levantadapela

divisãodeduasstringsenãoétratadanoexcepteportantoérelançadadepoisqueofinallyéexecutado.

Emaplicaçõesreais,o finallyéútilpara liberar recursosexternos(comoarquivosouconexõesde

VocêpodetambémfazerocursodatadessaapostilanaCaelum

13.5PARASABERMAIS:FINALLY

13.5PARASABERMAIS:FINALLY 185

Page 193: Python e Orientação a Objetos

rede),independentementedousodorecursotersidobemsucedidoounão.

NoPython,todasasexceçõessãoinstânciasdeumaclassederivadadeBaseException.Todavia,

ela não serve para ser diretamente herdada por exceções criadas por programadores. Para isso,utilizamosException,quetambéméfilhadeBaseException.

AbaixoestáahierarquiadeclassesdeexceçõesdoPython.Paramaisinformaçõessobrecadaumadelas,consulteadocumentação:https://docs.python.org/3/library/exceptions.html

BaseException+--SystemExit+--KeyboardInterrupt+--GeneratorExit+--Exception+--StopIteration+--StopAsyncIteration+--ArithmeticError|+--FloatingPointError|+--OverflowError|+--ZeroDivisionError+--AssertionError+--AttributeError+--BufferError+--EOFError+--ImportError|+--ModuleNotFoundError+--LookupError|+--IndexError|+--KeyError+--MemoryError+--NameError|+--UnboundLocalError+--OSError|+--BlockingIOError|+--ChildProcessError|+--ConnectionError||+--BrokenPipeError||+--ConnectionAbortedError||+--ConnectionRefusedError||+--ConnectionResetError|+--FileExistsError|+--FileNotFoundError|+--InterruptedError|+--IsADirectoryError|+--NotADirectoryError|+--PermissionError|+--ProcessLookupError|+--TimeoutError+--ReferenceError+--RuntimeError|+--NotImplementedError|+--RecursionError+--SyntaxError|+--IndentationError|+--TabError+--SystemError

13.6ÁRVOREDEEXCEÇÕES

186 13.6ÁRVOREDEEXCEÇÕES

Page 194: Python e Orientação a Objetos

+--TypeError+--ValueError|+--UnicodeError|+--UnicodeDecodeError|+--UnicodeEncodeError|+--UnicodeTranslateError+--Warning+--DeprecationWarning+--PendingDeprecationWarning+--RuntimeWarning+--SyntaxWarning+--UserWarning+--FutureWarning+--ImportWarning+--UnicodeWarning+--BytesWarning+--ResourceWarning

1. Na classe Conta, modifique o método deposita(). Ele deve lançar uma exceção chamada

ValueError,que jáfazpartedabibliotecapadrãodoPython,semprequeovalorpassadocomo

argumentoforinválido(porexemplo,quandofornegativo):

defdeposita(self,valor):if(valor<0):raiseValueErrorelse:self._saldo+=valor

2. Da maneira com está, apenas saberemos que ocorreu um ValueError, mas não saberemos o

motivo.Vamosacrescentarumamensagemparadeixaroerromaisclaro:

defdeposita(self,valor):if(valor<0):raiseValueError('Vocêtentoudepositarumvalornegativo.')else:self._saldo+=valor

Obs.:modifiquetambémaimplementaçãodométododeposita()daclasseContaPoupancada

mesmamaneiraquefizemosnaclasseConta,lançandoumValueErrorcasoovalorpassadoseja

negativo.

3. Façaomesmoparaométodosaca()daclasseContaCorrente,afinaloclientetambémnãopode

sacarumvalornegativo.

defsaca(self,valor):if(valor<0):raiseValueError('Vocêtentousacarumvalornegativo.')else:self._saldo-=valor

4. Vamosvalidartambémqueoclientenãopodesacarumvalormaiordoqueosaldodisponívelemconta.CriesuaprópriaexceçãochamadaSaldoInsuficienteError.Napastasrc,crieoarquivo

13.7EXERCÍCIOS:EXCEÇÕES

13.7EXERCÍCIOS:EXCEÇÕES 187

Page 195: Python e Orientação a Objetos

excecoes.pyeaclasseSaldoInsuficienteErrorquedeveherdardeRuntimeError.

classSaldoInsuficienteError(RuntimeError):pass

NométodosacadaclasseContaCorrente,vamosutilizarestanovaexceção:

classContaCorrente(Conta):#códigoomitido

defsaca(self,valor):if(valor<0):raiseValueError('Vocêtentousacarumvalornegativo.')if(self._saldo<valor):raiseSaldoInsuficienteError('Saldoinsuficiente.')self._saldo-=(valor+0.10)

Obs.:modifiquetambémaimplementaçãodométodosaca()daclasseConta,lançandooserros

ValueError e SaldoInsuficienteError da mesma maneira que fizemos na classe

ContaCorrente.

5. AgoracrieumaContaCorrenteechameométodosaca(),passandoumvalornegativo:

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)

valor=-1000.0cc.saca(valor)

Oqueacontece?

6. Precisamostrataroerroparanãopararaexecuçãodoprograma.Utilizeumblocotry/exceptpara

isso:

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)

valor=-1000.0

try:cc.saca(valor)print('Saquede{}realizadocomsucesso'.format(valor))exceptValueError:print('Ovalorasersacadodeveserumnúmeropositivo.')

Rodeoprogramaacimaevejaoqueacontece.

7. Agoratentesacarumvalormaiordoqueosaldo:

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)

valor=5000.0

try:cc.saca(valor)print('Saquede{}realizadocomsucesso.'.format(valor))

188 13.7EXERCÍCIOS:EXCEÇÕES

Page 196: Python e Orientação a Objetos

exceptValueError:print('Ovalorasersacadodeveserumnúmeropositivo.')

Oqueacontece?

8. ModiqueoprogramaefaçacomqueoerroSaldoInsuficienteErrornãosejalançadoquandoo

valorformaiordoqueosaldodaconta.

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)

valor=5000.0

try:cc.saca(valor)print('Saquede{}realizadocomsucesso.'.format(valor))exceptValueError:print('Ovalorasersacadodeveserumnúmeropositivo.')exceptSaldoInsuficienteError:print('Vocênãopossuisaldosuficienteparaconcluirestaoperação.')

Nãoesqueçadefazerosimportsnecessários.

9. Façatestescomométododeposita().Tentedepositarumvalornegativoevejaoqueacontece.

if__name__=='__main__':cc=ContaCorrente('123-4','João',1000.0)

valor=-1000.0

try:cc.deposita(valor)print('Depósitode{}realizadocomsucesso.'.format(valor))exceptValueError:print('Ovaloraserdepositadodeveserumnúmeropositivo.')

10. (Desafio)Este código simula uma operação de saque feito por um caixa eletrônico e poderia serencapsuladoemumaclasseque representeocaixa.CrieumaclassechamadaCaixaEletronico

comosmétodosque representeasoperaçõesdedepósitoesaque.Depois, instancieumobjetodotipoCaixaEletronicoetesteseusmétodos.

13.7EXERCÍCIOS:EXCEÇÕES 189

Page 197: Python e Orientação a Objetos

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

ErrosdesintaxeUmdoserrosmaiscomunséoSyntaxError.Geralmentesuasmensagensnãodizemmuito,amaiscomuméaSyntaxError:invalidsyntax.Poroutro lado,amensagem

diz o local onde o problema ocorreu. - onde o Python encontrou o problema. São descobertosquando o interpretador está traduzindo o código fonte para o bytecode. Indicamque há algo deerradocomaestruturadoprograma.Porexemplo:esquecerdefecharaspas,simplesouduplas,nahorade imprimirummensagem;esquecerdecolocardoispontos (":")aofinaldeuma instruçãoif,whileoufor,etc...

Erro semântico Este erro é quando o programa não se comporta como esperado. Aqui não élançada uma exceção, o programa apenas não faz a coisa certa. Sãomais difíceis de encontrarporqueointerpretadornãofornecenenhumainformaçãojáquenãosabeoqueoprogramadeveriafazer.Sãoerrosnaregradenegócio.Utilizarafunçãoprint()emalgunslugaresdocódigoonde

vocêsuspeitaqueestágerandooerropodeajudar.

OdepuradordoPython,opdb,éummóduloembutidoquefuncionacomoumconsoleinterativoondeépossívelrealizardebugdecódigospython.Vocêpodelermaisarespeitonadocumentação:https://docs.python.org/3/library/pdb.html

Seuslivrosdetecnologiaparecemdoséculopassado?

13.8OUTROSERROS

13.9PARASABERMAIS-DEPURADORDOPYTHON

190 13.8OUTROSERROS

Page 198: Python e Orientação a Objetos

CAPÍTULO14

Objetivos:

conheceromódulocollectionsconheceromódulocollections.abc

No capítulo 5, vimos uma introdução das principais estruturas de dados do Python como listas,tuplas,conjuntosedicionários.TambémaprendemosemorientaçãoaobjetosquetudoemPythonéumobjeto,inclusiveestasestruturas.

O Python possui uma biblioteca chamada collections , que reúne outros tipos de dados

alternativosaojáapresentadosnocapítulo5.Essestipostrazemnovasfuncionalidadesquepodemserimplementadasparamelhoraraeficiênciadonossocódigo.

Omódulocollectionstambémprovêummódulodeclassesabstratas,omóduloabc.collections,

quepodemserusadaspara testarsedeterminadaclasseprovêumainterfaceparticular.Aprenderemosumpoucosobreelaseseuuso.

AsestruturasdedadospadrãodoPythonsãodegrandevaliaemuitoutilizadasnalinguagem,masexistem momentos em que precisamos de funcionalidades extras que são comuns de projeto paraprojeto.Nessesentido,surgeomódulocollections,praacrescentaressasfuncionalidades.

Porexemplo,nolivroPythonFluentedeLucianoRamalho,éapresentadoumexemploemque

precisamosacrescentarfuncionalidadesextras:

COLLECTIONS

14.1USERLIST,USERDICTEUSERSTRING

14COLLECTIONS 191

Page 199: Python e Orientação a Objetos

"Um caso de uso concreto é o projeto Pingo.io (http://www.pingo.io/docs/), em que uma placaprogramável com pinos GPIO (por exemplo, Raspberry Pi ou Arduino) é representada por umobjeto board com um atributo board.pins ; esse atributo contém um mapeamento das

localizaçõesfísicasdospinosparaobjetosquerepresentamospinos.Alocalizaçãofísicapodeserumnúmeroouumastring como"A0"ou"P912".Porquestõesdeconsistência, édesejável quetodas as chaves com board.pins sejam _strings, mas é conveniente que a consulta a

my_arduino.pin[13] também funcione de modo que programadores iniciantes não tenham

problemasquandoquiseremfazerpiscaroLEDnopino13deseusArduinos."

Precisamos usar índices que são strings, portanto, um dicionário. Além disso, nosso dicionáriopoderiaapenasaceitarstringscomochavesparaesteobjetivoespecífico.Paranãotratarissoduranteaexecuçãodenossoprograma,podemoscriarumaclassequetenhaocomportamentodeumdicionáriocomessacaracterísticaespecífica.

Para isso, criamos uma classe que herda de uma classe chamada UserDict do pacote

collections:

classMeuDicionario(UserDict):pass

AclasseUserDict nãoherdadedict,mas simulaumdicionário.AUserDict possui uma

instânciadedictinternachamadadata,quearmazenaositenspropriamenteditos.

Criarsubclassesdetiposembutidoscomodictoulistdiretamenteépropensoaerros,porque

seusmétodos geralmente ignoramas versões sobrescritas.Alémde que cada implementação pode secomportardemaneiradiferente.OfatodeherdarmosdeUserDictenãodiretamentededictépara

evitarmosessesproblemas.

Criandoaclassedestamaneira,temosumaclassenossaquefuncionacomoumdicionário.Masnãofazsentidocriá-lasemacrescentarfuncionalidades,jáqueoPythonjápossuiessaestruturaprontaqueéodict.

Vamoscriarnossodicionário,demodoquesóaceitechavescomostrings evai representaros

pinosdaplacadoRasbperryPi,porexemplo:

fromcollectionsimportUserDict

classPins(UserDict):

def__contains__(self,key):returnstr(key)inself.keys()

def__setitem__(self,key,value):

192 14.1USERLIST,USERDICTEUSERSTRING

Page 200: Python e Orientação a Objetos

self.data[str(key)]=value

Notequeasobrescritade__setitem__garantequeachavesempreseráumastring.Podemos

testaressaclasse:

if__name__=='__main__':pins=Pins(one=1)print(pins)pins[3]=1lista=[1,2,3]pins[lista]=2print(pins)

Percebaquequandoimprimimosodicionário,todassuaschavessãostrings.

Se você está gostando dessa apostila, certamente vai aproveitar os cursosonlinequelançamosnaplataformaAlura.VocêestudaaqualquermomentocomaqualidadeCaelum.Programação,Mobile,Design,Infra,Front-Ende

Business,entreoutros!Ex-estudantedaCaelumtem10%dedesconto,sigaolink!

ConheçaaAluraCursosOnline.

Outros tipos que existem no módulo collections são: defaultdict , counter , deque e

namedtuple.

Aocontráriododict,nodefaultdictnãoénecessárioverificarseumachaveestápresenteou

não.

fromcollectionsimportdefaultdict

cores=[('1','azul'),('2','amarelo'),('3','vermelho'),('1','branco'),('3','verde')]

cores_favoritas=defaultdict(list)

forchave,valorincores:cores_favoritas[chave].append(valor)

print(cores_favoritas)

Ocódigovaigerarasaída:

[('1',['azul','branco']),('2',['amarelo']),('3',['vermelho','verde'])]

Agoraéamelhorhoradeaprenderalgonovo

14.2PARASABERMAIS

14.2PARASABERMAIS 193

Page 201: Python e Orientação a Objetos

SemacusarKeyError.

Counter

O Counter é um contador e permite contar as ocorrências de um determinado item em uma

estruturadedados:

fromcollectionsimportCounter

cores=['amarelo','azul','azul','vermelho','azul','verde','vermelho']

contador=Counter(cores)

print(contador)

Vaiimprimir:

Counter({'azul':3,'vermelho':2,'amarelo':1,'verde':1})

UmCounteréumdict epode receberumobjeto iterávelouummapacomoargumentopara

realizaracontagemdeseuselementos.

deque

Odequeéumaestruturadedadosqueforneceumafila comduasextremidadese épossível

adicionareremoverelementosdeambososlados:

fromcollectionsimportdeque

fila=deque()

fila.append('1')fila.append('2')fila.append('3')

print(len(fila))#saída:3

fila.pop()#excluielementodadireita

fila.append('3')#adicionaelementonadireita

fila.popleft()#excluielementodaesquerda

fila.appendleft('1')#adicionaelementonaesquerda

namedtuple

Anamedtuple,comoonomesugere,sãotuplasnomeadas.Nãoénecessáriousaríndicesinteiros

paraacessarseuselementosepodemosutilizarstrings-similaraosdicionários.Masaocontráriodosdicionários,namedtupleéimutável:

fromcollectionsimportnamedtuple

Conta=namedtuple('Conta','numerotitularsaldolimite')conta=Conta('123-4','João',1000.0,1000.0)

194 14.2PARASABERMAIS

Page 202: Python e Orientação a Objetos

print(conta)#saída:Conta(numero='123-4',titular='João',saldo=1000.0,limite=1000.0)

print(conta.titular)#saída:João

Note que para acessar o elemento nomeado utilizamos o operador '.' (ponto).Umanamedtuple

possuidoisargumentosobrigatóriosquesão:onomedatuplaeseuscampos(separadosporvírgulaouespaço).Noexemplo,a tupla sechamaConta epossui4campos:numero,titular,saldo e

limite.Comosãoimutáveis,nãopodemosmodificarosvaloresdeseuscampos:

conta.titular="José"

Issovaigeraroseguinteerro:

Traceback(mostrecentcalllast):File<stdin>,line5,in<module>conta.titular="José"AttributeError:can'tsetattribute

Anamedtupletambémécompatívelcomumatuplanormal.Issoquerdizerquevocêtambémpode

usaríndicesinteirosparaacessarseuselementos.

print(conta[0])#saída:'123-4'

Maisdetalhesdecadaumadessasestruturasestãonadocumentaçãoepodeseracessadasporestelink: https://docs.python.org/3/library/collections.html.Outra alternativa é usar a função help() no

objetoparaacessaradocumentação.

Omódulocollections.abc fornece classes abstratas quepodem ser usadaspara testar se uma

classeforneceumainterfaceespecífica.Porexemplo,seelaéiterávelounão.

Imaginequeobanconosentregouumarquivocomváriosfuncionáriosepediuquecalculássemosabonificação de cada umdeles. Precisamos acrescentar este arquivo emnossa aplicação para iniciar aleitura.

Conteúdodoarquivofuncionarios.txt:

João,111111111-11,2500.0Jose,222222222-22,3500.0Maria,333333333-33,4000.0Pedro,444444444-44,2500.0Mauro,555555555-55,1700.0Denise,666666666-66,3000.0Tomas,777777777-77,4200.0

CadalinhadoarquivorepresentaumFuncionariocomseusatributosseparadosporvírgula.Este

arquivo está no padrão Comma-separated-values, também conhecido como csv e são comumenteusados.OPythondásuportedeleituraparaeste tipodearquivo.Assim,vamosacrescentaromódulo

14.3COLLECTIONSABC

14.3COLLECTIONSABC 195

Page 203: Python e Orientação a Objetos

csv,quevaiajudarnatarefadeleroarquivo:

importcsv

arquivo=open('funcionarios.txt','r')leitor=csv.reader(arquivo)

forlinhainleitor:print(linha)

arquivo.close()

Oprogramaacimaabreumarquivoeum leitordomódulocsv, oreader - recebeo arquivo

comoparâmetroedevolveumleitorquevai ler linhaa linhaeguardarseuconteúdo.Podemos iterarsobreesteleitorepedirparaimprimiroconteúdodecadalinha-queéexatamenteoqueéfeitonolaçofor.Porúltimo,fechamosoarquivo.

Asaídaserá:

['João','111111111-11','2500.0']['Jose','222222222-22','3500.0']['Maria','333333333-33','4000.0']['Pedro','444444444-44','2500.0']['Mauro','555555555-55','1700.0']['Denise','666666666-66','3000.0']['Tomas','777777777-77','4200.0']

Reparequeoreaderguardacadalinhadeumarquivoemumalista,ecadavalordelimitadopor

vírgulasetornaumelementodestalista,oquefacilitaoacessoaosdados.

Agoracomestesdadosemmãos,podemosconstruirnossosobjetosdetipoFuncionario:

forlinhainleitor:funcionario=Funcionario(linha[0],linha[1],float(linha[2]))

Masaindaprecisamosdeumaestruturaparaguardá-los.Vamosutilizarumalista:

funcionarios=[]

forlinhainleitor:funcionario=Funcionario(linha[0],linha[1],float(linha[2]))funcionarios.append(funcionario)

Porfim,imprimimosossaldosdalista:

forfinfuncionarios:print(f.salario)

Acontecequenadaimpede,posteriormente,deinserirmosnestalistaqualqueroutroobjetoquenãoumfuncionário:

funcionarios.append('Python')funcionarios.append(1234)funcionarios.append(True)

Alist da bibliotecapadrão aceita qualquer tipodeobjeto comoelemento.Nãoqueremos este

196 14.3COLLECTIONSABC

Page 204: Python e Orientação a Objetos

comportamento, já que iremos calcular a bonificaçãode cadaumdeles, ondedependendodo tipodeobjetosinseridonalista,geraráerros.

OidealéquetivéssemosumaestruturadedadosqueaceitasseapenasobjetosdetipoFuncionario.Omódulocollections.abcfornececlassesabstratasquenosajudamaconstruirestruturasespecíficas,

comcaracterísticasdaregradenegóciodaaplicação.

Omódulocollections.abcpossuiumaclasseabsratachamadaContainer.UmContaineré

qualquer objeto que contém um número arbitrário de outros objetos. Listas, tuplas, conjuntos edicionários são tipos de containers. A classe Container suporta o operador in com o método

__contains__.

PrecisamosconstruirumcontainerdeobjetosdetipoFuncionario.Podemosconstruirumaclasse

querepresentaráessaestrutura,quedevesersubclassedeContainer:

fromcollections.abcimportContainer

classFuncionarios(Container):pass

if__name__=='__main__':funcionarios=Funcionarios()

OcódigoacimaacusaumTypeError:

TypeError:Can'tinstantiateabstractclassFuncionarioswithabstractmethods__contains__

Precisamos implementar ométodo__contains__, já que Funcionarios deve implementar a

classeabstrataContainer.Aideiaéquenossocontainersecomportecomoumalista,entãoteremos

umatributodotipolistaemnossaclasseparaguardarosobjetoseimplementarométodocontains:

fromcollections.abcimportContainer

classFuncionarios(Container):

_dados=[]

def__contains__(self,item):returnself._dados.__contains__(self,item)

if__name__=='__main__':funcionarios=Funcionarios()

14.4CONSTRUINDOUMCONTAINER

14.4CONSTRUINDOUMCONTAINER 197

Page 205: Python e Orientação a Objetos

Editorastradicionaispoucoligamparaebooksenovastecnologias.Nãodominamtecnicamente o assunto para revisar os livros a fundo. Não têm anos deexperiênciaemdidáticascomcursos.ConheçaaCasadoCódigo,umaeditoradiferente,comcuradoriadaCaelumeobsessãoporlivrosdequalidadeapreçosjustos.

CasadoCódigo,ebookcompreçodeebook.

Otamanhodonossocontainertambéméumainformaçãoimportante.NossaclasseFuncionarios

devesaberretornaressevalor.UtilizamosaclasseabstrataSizedparagarantiressafuncionalidade.A

classeSizedprovêométodolen()atravésdométodoespecial__len__():

fromcollections.abcimportContainer

classFuncionarios(Container,Sized):

_dados=[]

def__contains__(self,item):returnself._dados.__contains__(self,item)

def__len__(self):returnlen(self._dados)

if__name__=='__main__':funcionarios=Funcionarios()

Além de conter objetos e saber retornar a quantidade de seus elementos, queremos que nossocontainer seja iterável, ou seja, que consigamos iterar sobre seus elementos em um laço for, porexemplo.Omódulocollection.abctambémprovêumaclasseabstrataparaestecomportamento,éa

classeIterable.Iterablesuportaiteraçãocomométodo__iter__:

fromcollections.abcimportContainer

classFuncionarios(Container,Sized,Iterable):

_dados=[]

EditoraCasadoCódigocomlivrosdeumaformadiferente

14.5SIZED

14.6ITERABLE

198 14.5SIZED

Page 206: Python e Orientação a Objetos

def__contains__(self,item):returnself._dados.__contains__(self,item)

def__len__(self):returnlen(self._dados)

def__iter__(self):returnself._dados.__iter__(self)

if__name__=='__main__':funcionarios=Funcionarios()

TodacoleçãodeveherdardessasclassesABCs:Container,IterableeSized.Ouimplementar

seusprotocolos:__contains__,__iter__e__len__.

Alémdessasclasses,existemoutrasque facilitamesse trabalhoe implementamoutrosprotocolos.Vejaahierarquiadeclassedomódulocollections.abc:

Figura14.1:legendadaimagem

Alémdoque já foi implementado,a ideiaéquenossaclasseFuncionario funcione comouma

listacontandoapenasobjetosdotipoFuncionario.Comoaprendemosnocapítulo4,umalistaéuma

sequência.Alémdeumasequência,éumasequênciamutável-podemosadicionarelementosemumalista.NossaclasseFuncionariotambémdevepossuiressafuncionalidade.

Segundo o diagrama de classes do módulo collections.abc , a classe que representa essa

estruturaéaMutableSequence.NotequeMutableSequenceherdadeSequencequerepresentauma

sequência;queporsuavezherdadeContainer,IterableeSized.

Figura14.2:legendadaimagem

Portanto, devemos implementar 5 métodos abstratos (em itálico na imagem) segundo adocumentaçãodeMutableSequence:__len__,__getitem__,__setitem__,__delitem__ e

insert.Ométodo__getitem__garantequeaclasseéumContainereIterable. Segundo a

14.6ITERABLE 199

Page 207: Python e Orientação a Objetos

PEP234(https://www.python.org/dev/peps/pep-0234/)umobjetopodeseriterávelcomumlaçoforseimplementa__iter__ou__getitem__.

Então,bastanossaclasseFuncionarioherdardeMutableSequenceeimplementarseusmétodos

abstratos:

fromcollections.abcimportMutableSequence

classFuncionarios(MutableSequence):

_dados=[]

def__len__(self):returnlen(self._dados)

def__getitem__(self,posicao):returnself._dados[posicao]

def__setitem__(self,posicao,valor):self._dados[posicao]=valor

def__delitem__(self,posicao):delself._dados[posicao]

definsert(self,posicao,valor):returnself._dados.insert(posicao,valor)

E podemos voltar ao nosso código para acrescentar os dados de um arquivo em nosso containerFuncionarios:

importcsv

arquivo=open('funcionarios.txt','r')leitor=csv.reader(arquivo)

funcionarios=Funcionarios()

forlinhainleitor:funcionario=Funcionario(linha[0],linha[1],float(linha[2]))funcionarios.append(funcionario)

arquivo.close()

Ométodoinsert() garante o funcionamento dométodo append(). E podemos imprimir os

valoresdossaláriosdecadafuncionário:

forfinfuncionarios:print(f.salario)

Masatéaquinãohánadadediferentedeumalistacomum.Aindanãohánadaqueimpeçadeinserirqualqueroutroobjetoemnossalista.NossaclasseFuncionariossecomportacomoumalistacomum.

A ideia de implementarmos as interfaces de collections.abc era exatamente modificar alguns

comportamentos.

Queremosquenossa lista de funcionários apenas aceite objetos da classeFuncionario. Vamos

200 14.6ITERABLE

Page 208: Python e Orientação a Objetos

sobrescreverosmétodos__setitem__()queatribuiuumvaloremdeterminadaposiçãonalista.Este

métodopodeapenasatribuiraumadeterminadaposiçãoumobjetoFuncionario.

Paraisso,vamosusarométodoisinstance()quevaiverificarseoobjetoaseratribuídoéuma

instância de Funcionario . Caso contrário, vamos lançar uma exceção TypeError com uma

mensagemdeerro:

def__setitem__(self,posicao,valor):if(isinstance(valor,Funcionario)):self._dados[posicao]=valorelse:raiseTypeError('ValoratribuídonãoéumFuncionario')

Agora, ao tentar atribuir uma valor a determinada posição de nossa lista, recebemos umTypeError:

funcionarios[0]='Python'

Saída:

Traceback(mostrecentcalllast):File<stdin>,line18,in__setitem__raiseTypeError('ValoratribuídonãoéumFuncionario')TypeError:ValoratribuídonãoéumFuncionario

Faremosomesmocomométodoinsert():

definsert(self,posicao,valor):if(isinstance(valor,Funcionario)):returnself._dados.insert(posicao,valor)else:raiseTypeError('ValorinseridonãoéumFuncionario')

EpodemostestarnossaclasseimprimindonãoapenasosaláriomasovalordabonificaçãodecadaFuncionarioatravésdométodoget_bonificacao()quedefinimosnoscapítulospassados:

if__name__=='__main__':importcsv

arquivo=open('funcionarios.txt','r')leitor=csv.reader(arquivo)

funcionarios=Funcionarios()

forlinhainleitor:funcionario=Funcionario(linha[0],linha[1],float(linha[2]))funcionarios.append(funcionario)

print('salário-bonificação')forfinfuncionarios:print('{}-{}'.formar(f.salario,f.get_bonificacao()))

arquivo.close()

AsclassesABCsforamcriadasparaencapsularconceitosgenéricoseabstraçõescomoaprendemosnocapítulode classes abstratas.Sãocomumenteutilizadas emgrandes aplicações e frameworkspara

14.6ITERABLE 201

Page 209: Python e Orientação a Objetos

garantiraconsistênciadosistemaatravésdosmétodosisinstance()eissubclass().Nodiaadia

é raramenteusadoebastaousocorretodasestruturas já fornecidaspelabibliotecapadrãodoPythonparaamaiorpartedastarefas.

1. Vánapastanocursoecopieoarquivocontas.txtnapastasrcdoprojetobancoquecontém

váriosdadosdecontascorrentesdeclientesdobanco.

2. Crieumarquivochamadocontas.pynapastasrcdoprojetobanco.Crie uma classe chamada

ContasqueherdedaclasseabstrataMutableSequence:

fromcollections.abcimportMutableSequence

classContas(MutableSequence):pass

3. Vamoscriarumatributonaclassechamado_dadosdotipolistparaarmazenarnossascontas:

fromcollections.abcimportMutableSequence

classContas(MutableSequence):

_dados=[]

4. TenteinstanciarumobjetodetipoContas:

if__name__=='__main__':contas=Contas()

Note que não podemos instanciar este objeto. A interface MutableSequence nos obriga a

implementaralgunsmétodos:

Traceback(mostrecentcalllast):File<stdin>,line44,in<module>contas=Contas()TypeError:Can'tinstantiateabstractclassContaswithabstractmethods__delitem__,__getitem__,__len__,__setitem__,insert

5. ImplementeosmétodosexigidospelainterfaceMutableSequencenaclasseContas:

fromcollections.abcimportMutableSequence

classContas(MutableSequence):

_dados=[]

def__len__(self):returnlen(self._dados)

def__getitem__(self,posicao):returnself._dados[posicao]

def__setitem__(self,posicao,valor):

14.7EXERCÍCIO:CRIANDONOSSASEQUÊNCIA

202 14.7EXERCÍCIO:CRIANDONOSSASEQUÊNCIA

Page 210: Python e Orientação a Objetos

self._dados[posicao]=valor

def__delitem__(self,posicao):delself._dados[posicao]

definsert(self,posicao,valor):returnself._dados.insert(posicao,valor)

Agoraconseguimosinstanciarnossaclassesemnenhumerro:

if__name__=='__main__':contas=Contas()

6. NossasequênciasódevepermitiradicionarelementosquesejamdotipoConta.Vamosacrescentar

essavalidaçãonosmétodos__setitem__einsert.CasoovalornãosejaumaConta,vamos

lançarumTypeErrorcomasdevidasmensagensdeerro:

def__setitem__(self,posicao,valor):if(isinstance(valor,Conta)):self._dados[posicao]=valorelse:raiseTypeError("valoratribuídonãoéumaConta")

definsert(self,posicao,valor):if(isinstance(valor,Conta)):returnself._dados.insert(posicao,valor)else:raiseTypeError('valorinseridonãoéumaConta')

7. Vamosiniciaraleituradosdadosdoarquivoparaarmazenaremnossoobjetocontas:

if__name__=='__main__':importcsv

contas=Contas()

arquivo=open('contas.txt','r')leitor=csv.reader(arquivo)

arquivo.close()

8. Vamos criar uma laço for para ler cada linha do arquivo e construir um objeto do tipoContaCorrente.

if__name__=='__main__':importcsvfromcontaimportContaConrrete

contas=Contas()

arquivo=open('contas.txt','r')leitor=csv.reader(arquivo)

forlinhainleitor:conta=ContaCorrente(linha[0],linha[1],float(linha[2]),float(linha[3]))

arquivo.close()

14.7EXERCÍCIO:CRIANDONOSSASEQUÊNCIA 203

Page 211: Python e Orientação a Objetos

9. Queremosinserircadacontacriadaemnossasequênciamutávelcontas.Vamospedirparaqueo

programaacrescentecadacontacriadaemcontas:

forlinhainleitor:conta=ContaCorrente(linha[0],linha[1],float(linha[2]),float(linha[3]))contas.append(conta)

arquivo.close()

10. Nossa classeContas implementaMutableSequence. Isso quer dizer que ela é iterável já que

MutableSequenceimplementaoprotocolo__iter__atravésdométodo__getitem__.Vamos

iterar através de uma laço for nossoobjetocontas e pedir para imprimir o saldo e o valor do

impostodecadaumadelas:

if__name__=='__main__':#códigoanterioromitido

arquivo.close()

print('saldo-imposto')

forcincontas:print('{}-{}'.format(c.saldo,c.get_valor_imposto()))

Quevaigerarasaída:

saldo-imposto1200.0-12.02200.0-22.01500.0-15.05300.0-53.07800.0-78.01700.0-17.02300.0-23.08000.0-80.04600.0-46.09400.0-94.0

11. (Opcional) Modifique o código do exercício anterior de modo que imprima o valor do saldoatualizadodascontas.

12. (Opcional) Faça omesmo com as contas poupanças.Crie um arquivo com extensão.csv com

algumas contas poupanças, faça a leitura, construa os objetos e acrescente em uma estrutura dedadosdotipoMutableSequence.

13. (Desafio Opcional) Refaça o exercício utilizando MutableMapping ao invés de

MutableSequence.

204 14.7EXERCÍCIO:CRIANDONOSSASEQUÊNCIA

Page 212: Python e Orientação a Objetos

AAluraoferececentenasdecursosonlineemsuaplataformaexclusivadeensinoquefavoreceoaprendizadocomaqualidadereconhecidadaCaelum.VocêpodeescolherumcursonasáreasdeProgramação,Front-end,Mobile,

Design&UX,Infra,Business,entreoutras,comumplanoquedáacessoatodososcursos.Ex-estudantedaCaelumtem10%dedescontonestelink!

ConheçaoscursosonlineAlura.

JáconheceoscursosonlineAlura?

14.7EXERCÍCIO:CRIANDONOSSASEQUÊNCIA 205

Page 213: Python e Orientação a Objetos

CAPÍTULO15

Caso você esteja iniciando seus estudos na linguagem Python ou começando um projeto novo,aconselhamosfortementequevocêutilizeoPython3.

OPython2vemsendochamadodePython legadoouPythonantigoporboapartedacomunidadeque está em constante atividade para fazer amigraçãoda base de código existente (bemgrande, porsinal)paraPython3.

Aconselhamos a leitura deste artigo para maiores detalhes:https://wiki.python.org/moin/Python2orPython3.

Aperguntacorretaaquié:QuandodevousaroPythonantigo?Earespostamaiscomumquevocêvaiencontraré:usePythonantigoquandovocênãotiverescolha.

Porexemplo,quandovocêtrabalharemumprojetoantigoemigrarparaanovaversãonãoforumaalternativa nomomento. Ou quando você precisa utilizar uma biblioteca que ainda não funciona noPython3 ou não está emprocesso demudança.Outro caso é quando seu servidor de hospedagem sópermiteusarPython2-aquioaconselháveléprocurarporoutroserviçoqueatendasuademanda.

Nomais,vocêencontrarámuitomaterialsobreoPython2nainterneteaospoucosvaiconhecendomelhorasdiferençasentreumaversãoeoutra.

Nesteartigovocêvai encontrar a respostahttps://docs.python.org/3/whatsnew/3.0.html.Masnestecapítulomostramosasdiferençasmaisbásicaseimportantesparavocêiniciarseusestudos.

APÊNDICE-PYTHON2OUPYTHON3?

15.1QUAISASDIFERENÇAS?

206 15APÊNDICE-PYTHON2OUPYTHON3?

Page 214: Python e Orientação a Objetos

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

No Python2 o comando print funciona de maneira diferente já que não é uma função. Para

imprimiralgofazemos:

#nopython2>>>print"HelloWorld!"HelloWorld!

NoPython3printéumafunçãoeutilizamososparêntesescomodelimitadores:

#nopython3>>>print("HelloWorld!")HelloWorld!

Afunçãoraw_inputdoPython2foirenomeadaparainput()noPython3:

#nopython2>>>nome=raw_input("Digiteseunome:")

NoPython3:

#nopython3>>>nome=input("Digiteseunome:")

NoPython2adivisãoentrenúmerosdecimaisédiferenteentreumnúmerodecimaleuminteiro:

#nopython2>>>5/22>>>5/2.02.5

VocêpodetambémfazerocursodatadessaapostilanaCaelum

15.2AFUNÇÃOPRINT()

15.3AFUNÇÃOINPUT()

15.4DIVISÃODECIMAL

15.2AFUNÇÃOPRINT() 207

Page 215: Python e Orientação a Objetos

No Python3 a divisão tem o mesmo comportamento da matemática. E se quisermos o resultadointeirodadivisãoutilizamos//:

#nopython3>>>5/22.5>>>5//22

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

NoPython2suasclassesdevemherdardeobject:

#nopython2>>>classMinhaClasse(object):defmetodo(self,attr1,attr2):returnattr1+attr2

NoPython3essaherançaéimplícita,nãoprecisandoherdarexplicitamentedeobject:

#nopython3>>>classMinhaClasse():defmetodo(self,attr1,attr2):returnattr1+attr2

Seuslivrosdetecnologiaparecemdoséculopassado?

15.5HERANÇA

208 15.5HERANÇA

Page 216: Python e Orientação a Objetos

CAPÍTULO16

OPython já vem instalado nos sistemas Linux eMacOSmas será necessário fazer o download daúltima versão (Python 3.6) para acompanhar a apostila. O Python não vem instalado por padrão noWindowseodownloaddeveráserfeitonositehttps://www.python.org/alémdealgumasconfiguraçõesextras.

OprimeiropassoéacessarositedoPython:https://www.python.org/.NasessãodeDownloadsjá

será disponibilizado o instalador específico do Windows automaticamente, portanto é só baixar oPython3,nasuaversãomaisatual.

Figura16.1:TeladedownloaddoPythonparaoWindows

Apósodownload ser finalizado, abra-oenaprimeira telamarqueaopçãoAddPython3.Xto

PATH.EssaopçãoéimportanteparaconseguirmosexecutaroPythondentrodoPromptdeComandodo

Windows. Caso você não tenha marcado esta opção, terá que configurar a variável de ambiente noWindowsdeformamanual.

APÊNDICE-INSTALAÇÃO

16.1INSTALANDOOPYTHONNOWINDOWS

16APÊNDICE-INSTALAÇÃO 209

Page 217: Python e Orientação a Objetos

Figura16.2:Checkboxselecionado

Selecioneainstalaçãocustomizadasomenteparaverainstalaçãocommaisdetalhes.

Figura16.3:Instalaçãocustomizada

Natelaseguintesãoasfeaturesopcionais,secertifiquequeogerenciadordepacotespip esteja

selecionado, ele que permite instalar pacotes e bibliotecas no Python. Clique em Next para dar

210 16.1INSTALANDOOPYTHONNOWINDOWS

Page 218: Python e Orientação a Objetos

seguimentonainstalação.

Figura16.4:OptionalFeatures

Jána terceira tela,deixe tudocomoestá,masseatenteaodiretóriode instalaçãodoPython,paracasoqueiraprocuraroexecutáveloualgoqueenvolvaoseudiretório.

Figura16.5:AdvancedOptions

16.1INSTALANDOOPYTHONNOWINDOWS 211

Page 219: Python e Orientação a Objetos

Porfim,bastaclicaremInstalleaguardarotérminodainstalação.

Figura16.6:InstalaçãoConcluídacomSucesso

Terminadaainstalação,testeseoPythonfoiinstaladocorretamente.AbraoPromptdeComandoeexecute:

python-V

OBS:ParaquefuncionecorretamenteénecessárioquesejanoPromptdeComandoenãoemalgumprogramaGitBashinstaladoemsuamáquina.Eocomandopython-Véimportantequeestejacomo

Vcomletramaiúscula.

Esse comando imprime a versão do Python instalada no Windows. Se a versão for impressa,significaqueoPythonfoiinstaladocorretamente.Agora,rodeocomandopython:

python

AssimvocêteráacessoaoconsoledopróprioPython,conseguindoassimutilizá-lo.

212 16.1INSTALANDOOPYTHONNOWINDOWS

Page 220: Python e Orientação a Objetos

Querendoaprenderaindamaissobre?Esclarecerdúvidasdosexercícios?Ouvirexplicaçõesdetalhadascomuminstrutor?ACaelum oferece o cursodata presencial nas cidades de São Paulo, Rio deJaneiroeBrasília,alémdeturmasincompany.

ConsulteasvantagensdocursoPythoneOrientaçãoaObjetos

Os sistemasoperacionaisbaseadosnoDebian jápossuemoPython3pré-instalado.Verifique seoseusistemajápossuioPython3instaladoexecutandooseguintecomandonoterminal:

python3-V

OBS:Ocomandopython3-VéimportantequeestejacomoVcomletramaiúscula.

EstecomandoretornaaversãodoPython3instalada.Sevocêaindanãotivereleinstalado,digiteosseguintescomandosnoterminal:

sudoapt-getupdatesudoapt-getinstallpython3

AmaneiramaisfácildeinstalaroPython3noMacOSéutilizandooHomebrew.ComoHomebrew

instalado,abraoterminaledigiteosseguintescomandos:

brewupdatebrewinstallpython3

PodemosrodaroPythondiretamentedoseupróprioPrompt.

Podemos procurar pelo Python na caixa de pesquisa doWindows e abri-lo, assim o seu consolepróprioseráaberto.UmaoutraformaéabriraIDLEdoPython,queseparecemuitocomoconsolemasvemcomummenuquepossuialgumasopçõesextras.

VocêpodetambémfazerocursodatadessaapostilanaCaelum

16.2INSTALANDOOPYTHONNOLINUX

16.3INSTALANDOOPYTHONNOMACOS

16.4OUTRASFORMASDEUTILIZAROPYTHON

16.2INSTALANDOOPYTHONNOLINUX 213

Page 221: Python e Orientação a Objetos

Conheça aCasa do Código, uma nova editora, com autores de destaque nomercado, foco em ebooks (PDF, epub, mobi), preços imbatíveis e assuntosatuais.Coma curadoria daCaelum e excelentes autores, é uma abordagemdiferenteparalivrosdetecnologianoBrasil.

CasadoCódigo,LivrosdeTecnologia.

Seuslivrosdetecnologiaparecemdoséculopassado?

214 16.4OUTRASFORMASDEUTILIZAROPYTHON