Orientação a objetos e frameworks 1
Objetivo deste módulo Apresentar conceitos fundamentais de: orientação a objetos organização de programas em vários módulos Explicar conceitos a partir do nível básico para: programadores que nunca usaram OO programadores que já usaram OO em outras linguagens 2
Orientação a objetos: a origem Linguagem Simula 1967 Noruega: Ole-Johan Dahl e Kristen Nygaard objetos, classes, sub-classes métodos virtuais (funções associadas a objetos específicos em tempo de execução) 3
Orientação a objetos: evolução Smalltalk 1980 EUA, Xerox PARC (Palo Alto Research Center): Alan Kay, Dan Ingalls, Adele Goldberg et. al. terminologia: Object oriented programming message passing, late binding (a idéia por trás de métodos virtuais) 4
Smalltalk 1980 5
Squeak: Smalltalk livre 6
Conceito: objeto Um componente de software que inclui dados (atributos) e comportamentos (métodos) Em geral, os atributos são manipulados pelos métodos do próprio objeto (encapsulamento) Figuras: bycicle (bicicleta), The Java Tutorial http://docs.oracle.com/javase/tutorial/java/concepts/object.html 7
Exemplo: um objeto dict >>> d = {'AM':'Manaus', 'PE':'Recife', 'PR': 'Curitiba'} >>> d.keys() ['PR', 'AM', 'PE'] >>> d.get('pe') 'Recife' >>> d.pop('pr') 'Curitiba' >>> d {'AM': 'Manaus', 'PE': 'Recife'} >>> len(d) 2 >>> d. len () 2 Métodos: keys, get, pop, len etc. 8
Exemplo: um objeto dict Sobrecarga de operadores: [ ]: getitem >>> d {'AM': 'Manaus', 'PE': 'Recife'} setitem >>> d['am'] 'Manaus' >>> d. getitem ('AM') 'Manaus' >>> d['mg'] = 'Belo Horizonte' >>> d. setitem ('RJ', 'Rio de Janeiro') >>> d {'MG': 'Belo Horizonte', 'AM': 'Manaus', 'RJ': 'Rio de Janeiro', 'PE': 'Recife'} 9
Exemplo: um objeto dict Atributos de dados: class, doc >>> d. class <type 'dict'> >>> print d. doc dict() -> new empty dictionary. dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs. dict(seq) -> new dictionary initialized as if via: d = {} for k, v in seq: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) 10
Exemplo: um objeto dict Em Python, métodos também são atributos >>> dir(d) [' class ', ' cmp ', ' contains ', ' delattr ', ' delitem ', ' doc ', ' eq ', ' format ', ' ge ', ' getattribute ', ' getitem ', ' gt ', ' hash ', ' init ', ' iter ', ' le ', ' len ', ' lt ', ' ne ', ' new ', ' reduce ', ' reduce_ex ', ' repr ', ' setattr ', ' setitem ', ' sizeof ', ' str ', ' subclasshook ', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] 11
Exemplo: um objeto Tkinter.Label import Tkinter from time import strftime relogio = Tkinter.Label() relogio.pack() relogio['font'] = 'Helvetica 120 bold' relogio['text'] = strftime('%h:%m:%s') def tictac(): agora = strftime('%h:%m:%s') if agora!= relogio['text']: relogio['text'] = agora relogio.after(100, tictac) Note: Em Tkinter, atributos de dados são acessados via [ ]: getitem e setitem tictac() relogio.mainloop() 12
Objetos em linguagens Existem linguagens baseadas em objetos e linguagens orientadas a objetos baseadas em objetos: permitem que o programador use os tipos de objetos fornecidos, mas não permitem que ele crie seus próprios tipos de objetos Ex. Visual Basic antes da era.net 13
Objetos em Python Tudo é objeto: não existem tipos primitivos desde Python 2.2, dezembro de 2001 >>> 5 + 3 8 >>> 5. add (3) 8 >>> type(5) <type 'int'> 14
Funções são objetos >>> def fatorial(n):... '''devolve n!'''... return 1 if n < 2 else n * fatorial(n-1)... >>> fatorial(5) 120 >>> fat = fatorial >>> fat <function fatorial at 0x1004b5f50> >>> fat(42) 1405006117752879898543142606244511569936384000000000L >>> fatorial. doc 'devolve n!' >>> fatorial. name 'fatorial' >>> fatorial. code <code object fatorial at 0x1004b84e0, file "<stdin>", line 1> >>> fatorial. code.co_varnames ('n',) 15
Funções são objetos >>> fatorial. code.co_code ' \x00\x00d\x01\x00j\x00\x00o\x05\x00\x01d\x02\x00s\x01 \x00\x00t\x00\x00 \x00\x00d\x02\x00\x18\x83\x01\x00\x14s' >>> from dis import dis >>> dis(fatorial. code.co_code) 0 LOAD_FAST 0 (0) 3 LOAD_CONST 1 (1) 6 COMPARE_OP 0 (<) 9 JUMP_IF_FALSE 5 (to 17) Bytecode da 12 POP_TOP 13 LOAD_CONST 2 (2) 16 RETURN_VALUE função fatorial >> 17 POP_TOP 18 LOAD_FAST 0 (0) 21 LOAD_GLOBAL 0 (0) 24 LOAD_FAST 0 (0) 27 LOAD_CONST 2 (2) 30 BINARY_SUBTRACT 31 CALL_FUNCTION 1 34 BINARY_MULTIPLY 35 RETURN_VALUE >>> 16
Objetos têm tipo Tipagem forte: normalmente, Python não faz conversão automática entre tipos >>> a = 10 >>> b = '9' >>> a + b Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str' >>> a + int(b) 19 >>> str(a) + b '109' >>> 77 * None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for *: 'int' and 'NoneType' >>> 17
Tipagem dinâmica: variáveis não têm tipo >>> def dobro(x):... return x * 2... >>> dobro(7) 14 >>> dobro(7.1) 14.2 >>> dobro('bom') 'bombom' >>> dobro([10, 20, 30]) [10, 20, 30, 10, 20, 30] >>> dobro(none) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in dobro TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' 18
Duck typing Se voa como um pato, nada como um pato e grasna como um pato, é um pato. Tipagem dinâmica permite duck typing (tipagem pato) estilo de programação que evita verificar os tipos dos objetos, mas apenas seus métodos No exemplo anterior, a função dobro funciona com qualquer objeto x que consiga fazer x * 2 19
Tipagem forte x fraca Tipagem forte x fraca refere-se a conversão automática de valores de JavaScript (ECMAScript 5) em Node.js 0.6 tipos diferente. Linguagens de tipagem fraca são muito liberais na mistura entre tipos, e isso é uma fonte de bugs. Veja alguns resultados estranhos obtidos com JavaScript, que tem tipagem fraca. Em Python as três expressões acima geram TypeError, e as três últimas resultam False. Python tem tipagem forte. > 10 + '9' '109' > 10 + '9' * 1 19 > '10' + 9 * 1 '109' > 0 == '0' true > 0 == '' true > '0' == '' false 20
Tipagem forte x fraca, dinâmica x estática Tipagem forte x fraca refere-se a conversão automática de valores de tipos diferentes Tipagem dinâmica x estática refere-se à declaração dos tipos das variáveis, parâmetros formais e valores devolvidos pelas funções Linguagens de tipagem estática exigem a declaração dos tipos, ou usam inferência de tipos para garantir que uma variável será associada a somente a valores de um tipo 21
Tipagem em linguagens Smalltalk dinâmica forte Python dinâmica forte Ruby dinâmica forte C (K&R) estática fraca C (ANSI) estática forte Java estática forte C# estática forte JavaScript dinâmica fraca } combinação perigosa: PHP dinâmica fraca bugs sorrateiros 22
Conversões automáticas Python faz algumas (poucas) conversões automáticas entre tipos: Promoção de int para float Promoção de str para unicode assume o encoding padrão: ASCII por default >>> 6 * 7.0 42.0 >>> 'Spam, ' + u'eggs' u'spam, eggs' >>> 23
Funções para inspecionar objetos type(obj): devolve o tipo do objeto o tipo é uma classe dir(obj): devolve os nomes dos atributos do objeto métodos e atributos de dados devolve uma lista útil para uso interativo, mas não necessariamente completa 24
Objetos podem receber novos atributos Em geral, é possível atribuir valores a atributos não pré-definidos, em tempo de execução. Exceções: tipos embutidos, tipos com slots >>> fatorial <function fatorial at 0x1004b5f50> >>> fatorial._autor = 'Fulano de Tal' >>> fatorial._autor 'Fulano de Tal' >>> s = 'sapo' >>> s.nome = 'Joca' Traceback (most recent call last):... AttributeError: 'str' object has no attribute 'nome' 25
Acesso dinâmico a atributos getattr(obj, nome_do_atributo): acessar hasattr(obj, nome_do_atributo): testar setattr(obj, nome_do_atributo, valor): criar ou alterar delattr(obj, nome_do_atributo): remover 26
Acesso dinâmico >>> dobro <function dobro at 0x1004412a8> >>> dobro. doc 'devolve duas vezes x' >>> getattr(dobro, ' doc ') 'devolve duas vezes x' >>> hasattr(dobro, 'peso') False >>> setattr(dobro, '_inspecao', '2012-01-31') >>> dobro._inspecao '2012-01-31' >>> del dobro._inspecao >>> hasattr(dobro, '_inspecao') False 27
Acesso dinâmico >>> t = (1,) >>> type(t) <type 'tuple'> >>> for nome_atr in dir(t):... print nome_atr.ljust(16), getattr(t, nome_atr)... add <method-wrapper ' add ' of tuple object at... class <type 'tuple'> contains <method-wrapper ' contains ' of tuple object at... delattr <method-wrapper ' delattr ' of tuple object at... doc tuple() -> empty tuple tuple(iterable) -> tuple initialized from iterable's items subclasshook <built-in method subclasshook of type object at. If the argument is a tuple, the return value is the same object. eq <method-wrapper ' eq ' of tuple object at... format <built-in method format of tuple object at... [...] len <method-wrapper ' len ' of tuple object at... [...] str <method-wrapper ' str ' of tuple object at... count <built-in method count of tuple object at... index <built-in method index of tuple object at... 28
Conceito: classe Uma categoria, ou tipo, de objeto Uma idéia abstrata, uma forma platônica Exemplo: classe Cão : Eu digo: Ontem eu comprei um cão Você não sabe exatamente qual cão, mas sabe: é um mamífero, quadrúpede, carnívoro pode ser domesticado (normalmente) cabe em um automóvel 29
Exemplar de cão: instância da classe Cao >>> rex = Cao() instanciação 30
Classe Cao class Mamifero(object): """lição de casa: implementar""" instanciação class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) >>> rex = Cao('Rex') >>> rex Cao('Rex') >>> print rex Rex >>> rex.qt_patas 4 >>> rex.latir() Rex: Au! >>> rex.latir(2) Rex: Au! Au! >>> rex.nervoso = True >>> rex.latir(3) Rex: Au! Au! Au! Au! Au! Au! modulo2.git/cao.py 31
Classe Cao em Python class Mamifero(object): """lição de casa: implementar""" class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) atributos de dados na classe funcionam como valores default para os atributos das instâncas init é o construtor, ou melhor, o inicializador modulo2.git/cao.py 32
Classe Cao em Python class Mamifero(object): """lição de casa: implementar""" class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) self é o 1º parâmetro em todos os métodos de instância atributos da instância só podem ser acessados via self modulo2.git/cao.py 33
Classe Cao class Mamifero(object): """lição de casa: implementar""" class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) invocação >>> rex = Cao('Rex') >>> rex Cao('Rex') >>> print rex Rex >>> rex.qt_patas 4 >>> rex.latir() Rex: Au! >>> rex.latir(2) Rex: Au! Au! na invocação do método, a instância é passada implicitamente na posição do self modulo2.git/cao.py 34
Mamifero: superclasse de Cao class Mamifero(object): """lição de casa: implementar""" class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) UML diagrama de classe generalização modulo2.git/cao.py 35
Subclasses de Cao Continuação de cao.py class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def init (self, nome): Cao. init (self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses) Diz a lenda que o cão São Bernardo leva um pequeno barril de conhaque para resgatar viajantes perdidos na neve. 36
Subclasses de Cao class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def init (self, nome): Cao. init (self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses) >>> sansao = SaoBernardo('Sansao') >>> sansao.servir() Sansao serve o conhaque (restam 9 doses) >>> sansao.doses = 1 >>> sansao.servir() Sansao serve o conhaque (restam 0 doses) >>> sansao.servir() Traceback (most recent call last):... ValueError: Acabou o conhaque! 37
Subclasses de Cao Continuação de cao.py class Pequines(Cao): nervoso = True class Mastiff(Cao): def latir(self, vezes=1): # o mastiff não muda seu latido quando nervoso print self.nome + ':' + ' Wuff!' * vezes class SaoBernardo(Cao): def init (self, nome): Cao. init (self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses) 38
Relógio com classe import Tkinter from time import strftime class Relogio(Tkinter.Label): def init (self): Tkinter.Label. init (self) self.pack() self['text'] = strftime('%h:%m:%s') self['font'] = 'Helvetica 120 bold' self.tictac() def tictac(self): agora = strftime('%h:%m:%s') if agora!= self['text']: self['text'] = agora self.after(100, self.tictac) rel = Relogio() rel.mainloop() modulo2.git/relogio_oo.py 39
Uma pequena parte da hierarquia de classes do Tkinter herança múltipla mixin herança múltipla 40
Um pouco mais da hierarquia de classes do Tkinter 41
Hierarquia de classes dos objetos gráficos dotkinter 42
Herança múltipla Refatoração de cao.py para cao2.py Reutilizar o latido do mastiff em outros cães grandes mixin herança múltipla herança múltipla class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) class Grande(object): """ Mixin: muda o latido""" def latir(self, vezes=1): # faz de conta que cães grandes não mudam # seu latido quando nervosos print self.nome + ':' + ' Wuff!' * vezes class Mastiff(Grande, Cao): """ O mastiff é o maior cão que existe """ class SaoBernardo(Grande, Cao): def init (self, nome): Cao. init (self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses) modulo2.git/cao2.py 43
Herança múltipla class Cao(Mamifero): qt_patas = 4 carnivoro = True nervoso = False def init (self, nome): self.nome = nome def latir(self, vezes=1): # quando nervoso, late o dobro vezes = vezes + (self.nervoso * vezes) print self.nome + ':' + ' Au!' * vezes def str (self): return self.nome def repr (self): return 'Cao({0!r})'.format(self.nome) class Grande(object): """ Mixin: muda o latido""" def latir(self, vezes=1): # faz de conta que cães grandes não mudam # seu latido quando nervosos print self.nome + ':' + ' Wuff!' * vezes class Mastiff(Grande, Cao): """ O mastiff é o maior cão que existe """ class SaoBernardo(Grande, Cao): def init (self, nome): Cao. init (self, nome) self.doses = 10 def servir(self): if self.doses == 0: raise ValueError('Acabou o conhaque!') self.doses -= 1 msg = '{0} serve o conhaque (restam {1} doses)' print msg.format(self.nome, self.doses) modulo2.git/cao2.py 44
As duas hierarquias de um sistema OO Hierarquia de classes is-a: é um Hierarquia de objetos part-of: parte de Object-oriented Analysis and Design with Applications 2ed. - Grady Booch 45
As duas hierarquias de um sistema OO Object-oriented Analysis and Design with Applications 3ed. - Booch et. al. 46
from Tkinter import Frame, Label, Button Timer class Timer(Frame): def init (self): Frame. init (self) self.inicio = self.agora = 15 self.pendente = None # alarme pendente self.grid() self.mostrador = Label(self, width=2, anchor='e', font='helvetica 120 bold',) self.mostrador.grid(column=0, row=0, sticky='nswe') self.bt_start = Button(self, text='start', command=self.start) self.bt_start.grid(column=0, row=1, sticky='we') self.atualizar_mostrador() def atualizar_mostrador(self): self.mostrador['text'] = str(self.agora) def start(self): if self.pendente: self.after_cancel(self.pendente) self.agora = self.inicio self.atualizar_mostrador() self.pendente = self.after(1000, self.tictac) Exemplo simples de composição def tictac(self): self.agora -= 1 self.atualizar_mostrador() if self.agora > 0: self.pendente = self.after(1000, self.tictac) timer = Timer() timer.mainloop() modulo2.git/timer.py 47
from Tkinter import Frame, Label, Button Timer class Timer(Frame): def init (self): Frame. init (self) self.inicio = self.agora = 15 self.pendente = None # alarme pendente self.grid() self.mostrador = Label(self, width=2, anchor='e', font='helvetica 120 bold',) self.mostrador.grid(column=0, row=0, sticky='nswe') self.bt_start = Button(self, text='start', command=self.start) self.bt_start.grid(column=0, row=1, sticky='we') self.atualizar_mostrador() def atualizar_mostrador(self): self.mostrador['text'] = str(self.agora) def start(self): if self.pendente: self.after_cancel(self.pendente) self.agora = self.inicio self.atualizar_mostrador() self.pendente = self.after(1000, self.tictac) def tictac(self): self.agora -= 1 self.atualizar_mostrador() if self.agora > 0: self.pendente = self.after(1000, self.tictac) timer = Timer() timer.mainloop() modulo2.git/timer.py 48
Composição Arranjo de partes de um sistema mostrador = Label() componentes, sub-componentes... bt_start = Button() timer = Timer() 49
instanciação instanciação from Tkinter import Frame, Label, Button class Timer(Frame): def init (self): Frame. init (self) self.inicio = self.agora = 15 self.pendente = None # alarme pendente self.grid() self.mostrador = Label(self, width=2, anchor='e', font='helvetica 120 bold',) self.mostrador.grid(column=0, row=0, sticky='nswe') self.bt_start = Button(self, text='start', command=self.start) self.bt_start.grid(column=0, row=1, sticky='we') self.atualizar_mostrador() def atualizar_mostrador(self): self.mostrador['text'] = str(self.agora) def start(self): if self.pendente: self.after_cancel(self.pendente) self.agora = self.inicio self.atualizar_mostrador() self.pendente = self.after(1000, self.tictac) def tictac(self): self.agora -= 1 self.atualizar_mostrador() if self.agora > 0: self.pendente = self.after(1000, self.tictac) instanciação timer = Timer() timer.mainloop() modulo2.git/timer.py 50
Composição em UML composição composição 51