Luciano Ramalho
  luciano@ramalho.org




outubro/2012
                                                                ac to
                                                           mp
                Objetos Pythonicos                      co
          Orientação a objetos e padrões de projeto em Python
Conceito: “objeto”
• Um componente de software
  que inclui dados (campos) 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
Terminologia
pythonica
• Objetos possuem atributos
• Os atributos de um objeto
  podem ser:
 •  métodos                 funções associadas

 • atributos de dados                     “campos”


        Figuras: bycicle (bicicleta), The Java Tutorial
        http://docs.oracle.com/javase/tutorial/java/concepts/object.html
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.
Exemplo: um objeto dict
   • dir revela atributos de um dict
    • atributos de dados e métodos
>>> 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']
Exemplo: um objeto dict
                            • Sobrecarga de operadores:
                             • [ ]: __getitem__, __setitem__
>>> d
{'AM': 'Manaus', 'PE': 'Recife'}
>>> 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'}
Exemplo: um objeto dict
    • Atributos de dados: __class__, __doc__
 >>> d.__class__
 <type 'dict'>
 >>> type(d)
 <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)
Exemplo: outro dict
>>> d = dict()
>>> d.keys()
[]                                       • d = dict()
>>> d.get('bla')
>>> print d.get('bla')
                                           é o mesmo que
None                                       d = {}
>>> d.get('spam')
>>> print d.get('spam')
None
>>> d.pop('ovos')
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
KeyError: 'ovos'
>>> d
{}
>>> len(d)
0
>>> d.__len__()
0
Exercício: construir e
 controlar Tkinter.Label
import Tkinter

relogio = Tkinter.Label()

relogio.pack()
relogio['font'] = 'Helvetica 120 bold'
relogio['text'] = strftime('%H:%M:%S')

from time import strftime

def tic():
    relogio['text'] = strftime('%H:%M:%S')
    
def tac():                               Note: no Tkinter, atributos
    relogio.after(100, tictac)           de dados são acessados
                                         via [ ]:
tac()
relogio.mainloop()                       __getitem__ __setitem__
Tudo é um objeto
• Não existem “tipos primitivos” como em Java
 • desde Python 2.2, dezembro de 2001
          >>> 5 + 3
          8
          >>> 5 .__add__(3)
          8
          >>> type(5)
          <type 'int'>
Tipos embutidos
• Implementados em C, por eficiência
• Comportamentos fixos
• Texto: str, unicode
• Números: int, long, float, complex, bool
• Coleções: list, tuple, dict, set, frozenset
• etc.
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',)
Funções são objetos
>>> fatorial.__code__.co_code
'|x00x00dx01x00jx00x00ox05x00x01dx02x00Sx01|x00x00tx00x00|
x00x00dx02x00x18x83x01x00x14S'
>>> from dis import dis
>>> dis(fatorial.__code__.co_code)
          0 LOAD_FAST           0 (0)
          3 LOAD_CONST          1 (1)
          6 COMPARE_OP          0 (<)

                                              Bytecode da
          9 JUMP_IF_FALSE       5 (to 17)
         12 POP_TOP
         13 LOAD_CONST          2 (2)

    >>
         16 RETURN_VALUE
         17 POP_TOP                           função fatorial
         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
>>>
Tipagem forte
• O tipo de um objeto nunca muda
• Raramente Python faz conversão automática de
  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'
    >>>
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'
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 somente em tempo de execução, e
    não na compilação)
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” (invocação de métodos),
    “late binding” (métodos virtuais)
Smalltalk 1980
Squeak: Smalltalk livre
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
 • x implementa o método __mult__(n), para n
    inteiro
Tipagem forte x fraca                                   JavaScript
                                                     (ECMAScript 5)
• Tipagem forte x fraca refere-se a                  em Node.js 0.6
  conversão automática de valores de
  tipos diferente.
• Linguagens de tipagem fraca são muito
                                                    > 10 + '9'
                                                    '109'
                                                    > 10 + '9' * 1
  liberais na mistura entre tipos, e isso é         19
  uma fonte de bugs.                                > '10' + 9 * 1
                                                    '109'

       Veja alguns resultados estranhos obtidos     > 0 == '0'
         com JavaScript, que tem tipagem fraca.     true
                                                    > 0 == ''
     Em Python as três expressões acima geram       true
     TypeError, e as três últimas resultam False.   > '0' == ''
                      Python tem tipagem forte.     false
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
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
                                     combinação
 JavaScript
      PHP
              dinâmica
              dinâmica
                         fraca
                         fraca   }   perigosa:
                                     bugs sorrateiros
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'
         >>>
Tipos são classes
• Para criar um novo tipo de objeto, crie uma classe
• A função type devolve a classe de um objeto
  >>> class Mamifero(object):
  ...     pass
  ...
  >>> kingkong = Mamifero()
  >>> type(kingkong)
  <class '__main__.Mamifero'>
  >>> ClasseDoKingKong = type(kingkong)
  >>> dumbo = ClasseDoKingKong()
  >>> dumbo
  <__main__.Mamifero object at 0x10045dcd0>
  >>> type(dumbo)
  <class '__main__.Mamifero'>
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
Exemplar de cão:
instância da classe Cao




      >>> rex = Cao()   instanciação
instanciação   >>> rex = Cao('Rex')
                                                 >>> rex

 Classe Cao                                      Cao('Rex')
                                                 >>> print rex
                                                 Rex
                                                 >>> rex.qt_patas
class Mamifero(object):                          4
    """lição de casa: implementar"""             >>> rex.latir()
                                                 Rex: Au!
class Cao(Mamifero):
                                                 >>> rex.latir(2)
    qt_patas = 4
    carnivoro = True                             Rex: Au! Au!
    nervoso = False                              >>> rex.nervoso = True
    def __init__(self, nome):                    >>> rex.latir(3)
        self.nome = nome                         Rex: Au! Au! Au! Au! Au! Au!
    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)




                             oopy/exemplos/cao.py
Como atributos são
acessados
• Ao buscar o.a (atributo a do objeto o da classe C),
  o interpretador Python faz o seguinte:
• 1) acessa atributo a da instancia o; caso não exista...
• 2) acessa atributo a da classe C de o
  (type(o) ou o.__class__); caso nao exista...
• 3) busca o atributo a nas superclasses de C,
  conforme a MRO (method resolution order)
Classe Cao em Python
class Mamifero(object):
    """lição de casa: implementar"""
                                                 •   __init__ é o
class Cao(Mamifero):
    qt_patas = 4                                     construtor, ou melhor,
    carnivoro = True
    nervoso = False
                                                     o inicializador

                                                 •
    def __init__(self, nome):
        self.nome = nome
    def latir(self, vezes=1):
                                                     self é o 1º parâmetro
        # quando nervoso, late o dobro               formal em todos os
        vezes = vezes + (self.nervoso * vezes)
        print self.nome + ':' + ' Au!' * vezes       métodos de instância
    def __str__(self):
        return self.nome
    def __repr__(self):
        return 'Cao({0!r})'.format(self.nome)




                             oopy/exemplos/cao.py
>>> rex = Cao('Rex')
                                                         >>> rex

 Classe Cao                                              Cao('Rex')
                                                         >>> print rex
                                                         Rex
                                                         >>> rex.qt_patas
class Mamifero(object):                                  4
    """lição de casa: implementar"""                     >>> rex.latir()
                                                         Rex: Au!
class Cao(Mamifero):                       invocação     >>> rex.latir(2)
    qt_patas = 4                                         Rex: Au! Au!
    carnivoro = True
    nervoso = False


                                                 •
    def __init__(self, nome):
        self.nome = nome
    def latir(self, vezes=1):
                                                       na invocação do
        # quando nervoso, late o dobro                 método, a instância
        vezes = vezes + (self.nervoso * vezes)
        print self.nome + ':' + ' Au!' * vezes         é passada
    def __str__(self):
        return self.nome                               automaticamente
    def __repr__(self):
        return 'Cao({0!r})'.format(self.nome)
                                                       na posição do self


                               oopy/exemplos/cao.py
Classe Cao em Python
class Mamifero(object):
    """lição de casa: implementar"""             •   atributos de dados na
                                                     classe funcionam
class Cao(Mamifero):
    qt_patas = 4                                     como valores default
    carnivoro = True
    nervoso = False                                  para os atributos das
    def __init__(self, nome):
        self.nome = nome
                                                     instâncas

                                                 •
    def latir(self, vezes=1):
        # quando nervoso, late o dobro
        vezes = vezes + (self.nervoso * vezes)
                                                     atributos da instância
        print self.nome + ':' + ' Au!' * vezes       só podem ser
    def __str__(self):
        return self.nome                             acessados via self
    def __repr__(self):
        return 'Cao({0!r})'.format(self.nome)




                             oopy/exemplos/cao.py
Mamifero: superclasse
 de Cao
class Mamifero(object):
                       UML
                                                                  diagrama de classe
    """lição de casa: implementar"""




                                                 especialização
class Cao(Mamifero):




                                                                                       generalização
    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)




                             oopy/exemplos/cao.py
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                                   Diz a lenda que o cão
    def servir(self):                                     São Bernardo leva um
        if self.doses == 0:                               pequeno barril de conhaque
            raise ValueError('Acabou o conhaque!')
        self.doses -= 1                                   para resgatar viajantes
        msg = '{0} serve o conhaque (restam {1} doses)'   perdidos na neve.
        print msg.format(self.nome, self.doses)
Subclasses                    >>> sansao = SaoBernardo('Sansao')

 de Cao                        >>> sansao.servir()
                               Sansao serve o conhaque (restam 9 doses)
                               >>> sansao.doses = 1
                               >>> sansao.servir()
                               Sansao serve o conhaque (restam 0 doses)
                               >>> sansao.servir()
class Pequines(Cao):           Traceback (most recent call last):
    nervoso = True               ...
    
class Mastiff(Cao):            ValueError: Acabou o conhaque!
    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)
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)
Interface
                • Interface é um conceito essencial em OO
                 • não depende de uma palavra reservada
               A interface fornece uma separação entre a
               implementação de uma abstração e seus clientes.
               Ela limita os detalhes de implementação que os
               clientes podem ver.
               Também especifica a funcionalidade que as
               implementações devem prover.

P. S. Canning, W. R. Cook, W. L. Hill, and W. G. Olthoff. 1989. Interfaces for strongly-typed object-
oriented programming. In Conference proceedings on Object-oriented programming systems,
languages and applications (OOPSLA '89). ACM, New York, NY, USA, 457-467.
DOI=10.1145/74877.74924 http://doi.acm.org/10.1145/74877.74924
Interfaces e protocolos
• Em SmallTalk, as interfaces eram chamadas de
  “protocolos”.
 • Não há verificação de interfaces na linguagem,
    mas algumas IDEs (“browsers”) permitem
    agrupar os métodos por protocolo para facilitar
    a leitura
• Um protocolo é uma interface informal, não
  declarada porém implementada por métodos
  concretos
Interfaces em Python
• Conceitualmente, sempre existiram como
  protocolos
• Não havia maneira formal de especificar interfaces
  em Python até a versão 2.5
 • usava-se termos como “uma sequência” ou
    “a file-like object”
• Agora temos ABC (Abstract Base Class)
 • com herança múltipla, como em C++
Exemplo: tômbola
• Sortear um a um
  todos os itens de
  uma coleção finita,
  sem repetir
• A mesma lógica é
  usada em sistemas
  para gerenciar
  banners online
Interface da tômbola

• Carregar itens
• Misturar itens
• Sortear um item
• Indicar se há mais
  itens
Projeto da tômbola




• UML:
 diagrama de classe
TDD: Test Driven Design
• Metodologia de desenvolvimento iterativa na qual,
  para cada funcionalidade nova, um teste é criado
  antes do código a ser implementado
• Esta inversão ajuda o programador a desenvolver
  com disciplina apenas uma funcionalidade de cada
  vez, mantendo o foco no teste que precisa passar
• Cada iteração de teste/implementação deve ser
  pequena e simples: “baby steps” (passinhos de
  bebê)
Doctests
• Um dos módulos para fazer testes automatizados
  na biblioteca padrão de Python
 • o outro módulo é o unittest, da família xUnit
• Doctests foram criados para testar exemplos
  embutidos na documentação
• Usaremos doctests para especificar exercícios
• Exemplo: $ python -m doctest cao.rst
                  oopy/exemplos/cao.rst
Coding Dojo


• Implementação da classe Tombola, com testes
  feitos em Doctest
Implementação da tômbola
                      # coding: utf-8

                      import random

                      class Tombola(object):
                          itens = None

                          def carregar(self, itens):
                              self.itens = list(itens)

                          def carregada(self):
                              return bool(self.itens)

                          def misturar(self):


 • Python 2.2 a 2.7
                              random.shuffle(self.itens)

                          def sortear(self):
                              return self.itens.pop()
Iteração em C e Python
#include <stdio.h>

int main(int argc, char *argv[]) {
    int i;
    for(i = 0; i < argc; i++)
        printf("%sn", argv[i]);
    return 0;
}                import sys

                 for arg in sys.argv:
                     print arg @ramalhoorg
Iteração em Java

class Argumentos {
    public static void main(String[] args) {
        for (int i=0; i < args.length; i++)
            System.out.println(args[i]);
    }
}


             $ java Argumentos alfa bravo charlie
             alfa
             bravo
             charlie                      @ramalhoorg
ano:
Iteração em Java ≥1.5                          2004

 • Enhanced for (for melhorado)
class Argumentos2 {
    public static void main(String[] args) {
        for (String arg : args)
            System.out.println(arg);
    }
}


              $ java Argumentos2 alfa bravo charlie
              alfa
              bravo
              charlie                      @ramalhoorg
ano:
Iteração em Java ≥1.5                    2004

 • Enhanced for (for melhorado)
class Argumentos2 {
    public static void main(String[] args) {
        for (String arg : args)
            System.out.println(arg);
    }
}
                                        ano:
                      import sys        1991

                      for arg in sys.argv:
                          print arg @ramalhoorg
Exemplos de iteração
• Iteração em Python não se limita a tipos primitivos
• Exemplos
 • string
 • arquivo
 • Django QuerySet
 • Baralho (em: “OO em Python sem Sotaque”)
      https://slideshare.net/ramalho/
                                 @ramalhoorg
List comprehension
List comprehensions          ●   Compreensão de lista ou abrangência
                             ●   Exemplo: usar todos os elementos:
•   Expressões que consomem L2 = [n*10 for n in L]
                           – iteráveis e


    produzem listas
     qualquer iterável
    resultado: uma lista

>>> s = 'abracadabra'
>>> l = [ord(c) for c in s]
>>> [ord(c) for c in s]
[97, 98, 114, 97, 99, 97, 100, 97, 98, 114, 97]

                                                          @ramalhoorg
Set & dict comprehensions
• Expressões que consomem iteráveis e
  produzem sets ou dicts


 >>> s = 'abracadabra'
 >>> {c for c in s}
 set(['a', 'r', 'b', 'c', 'd'])
 >>> {c:ord(c) for c in s}
 {'a': 97, 'r': 114, 'b': 98, 'c': 99, 'd': 100}



                                           @ramalhoorg
Em Python o comando for
itera sobre... “iteráveis”
• Definicão preliminar informal:
 • “iterável” = que pode ser iterado
 • assim como:
    “desmontável” = que pode ser desmontado
• Iteráveis podem ser usados em outros contextos
  além do laço for

                                          @ramalhoorg
Tipos iteráveis embutidos
  • basestring   • frozenset
   • str         • list
   • unicode     • set
  • dict         • tuple
  • file          • xrange
                               @ramalhoorg
Funções embutidas que
consomem iteráveis
• all       • max
• any       • min
• filter     • reduce
• iter      • sorted
• len       • sum
• map       • zip
                        @ramalhoorg
Operações com iteráveis
                                       >>>   a, b, c = 'XYZ'
  • Desempacotamento                   >>>
                                       'X'
                                             a

     de tupla                          >>>   b
                                       'Y'

     • em atribuições                  >>>
                                       'Z'
                                       >>>
                                             c

                                             g = (n for n in [1, 2, 3])
     • em chamadas de funções          >>>
                                       >>>
                                             a, b, c = g
                                             a
>>> def soma(a, b):                    1
...     return a + b                   >>>  b
...                                    2
>>> soma(1, 2)                         >>>  c
3                                      3
>>> t = (3, 4)
>>> soma(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: soma() takes exactly 2 arguments (1 given)
>>> soma(*t)
                                                               @ramalhoorg
7
Em Python, um iterável é...
• Um objeto a partir do qual a função iter
  consegue obter um iterador.
• A chamada iter(x):
 • invoca x.__iter__() para obter um iterador
 • ou, se x.__iter__ não existe:
   • fabrica um iterador que acessa os itens de x
      sequenciamente fazendo x[0], x[1], x[2] etc.
                                             @ramalhoorg
Protocolo de sequência
 >>> t = Trem(4)
 >>> len(t)
 4
                       __len__
 >>> t[0]
 'vagao #1'
 >>> t[3]              __getitem__
 'vagao #4'
 >>> t[-1]
 'vagao #4'
 >>> for vagao in t:
 ...   print(vagao)
                       __getitem__
 vagao #1
 vagao #2
 vagao #3
 vagao #4



                                 @ramalhoorg
Protocolo
  de sequência
 • implementação “informal” da interface
class Trem(object):
    def __init__(self, num_vagoes):
        self.num_vagoes = num_vagoes
    def __len__(self):
        return self.num_vagoes
    def __getitem__(self, pos):
        indice = pos if pos >= 0 else self.num_vagoes + pos
        if 0 <= indice < self.num_vagoes: # indice 2 -> vagao #3
            return 'vagao #%s' % (indice+1)
        else:
                                                        @ramalhoorg
            raise IndexError('vagao inexistente %s' % pos)
Tômbola como sequência
• Permitir contagem
  de itens
• Permitir acesso
  direto a cada item
  pela ordem em que
  foi colocado

Sim, este é um exemplo
forçado... mas daqui a
pouco melhora!
Interface
  Sequence
  • collections.Sequence
from collections import Sequence

class Trem(Sequence):
    def __init__(self, num_vagoes):
        self.num_vagoes = num_vagoes
    def __len__(self):
        return self.num_vagoes
    def __getitem__(self, pos):
        indice = pos if pos >= 0 else self.num_vagoes + pos
        if 0 <= indice < self.num_vagoes: # indice 2 -> vagao #3
            return 'vagao #%s' % (indice+1)
        else:
                                                        @ramalhoorg
            raise IndexError('vagao inexistente %s' % pos)
Herança de
 Sequence
>>> t = Trem(4)
>>> 'vagao #2' in t
True
>>> 'vagao #5' in t
False
>>> for i in reversed(t): print i
...
vagao #4
vagao #3
                                     from collections import Seque
vagao #2
vagao #1
                                     class Trem(Sequence):
>>> t.index('vagao #2')
                                         def __init__(self, num_va
1
                                             self.num_vagoes = num
>>> t.index('vagao #7')
                                         def __len__(self):
Traceback (most recent call last):
                                             return self.num_vagoe
  ...
                                         def __getitem__(self, pos
ValueError                                             @ramalhoorg
                                             indice = pos if pos >
Interface
Iterable

• Iterable provê um método
  __iter__
• O método __iter__ devolve
  uma instância de Iterator



                              @ramalhoorg
Iterator é...

• um padrão de projeto

                         Design Patterns
                         Gamma, Helm, Johnson & Vlissides
                         Addison-Wesley,
                         ISBN 0-201-63361-2




                                              @ramalhoorg
Head First
Design Patterns
Poster
O'Reilly,
ISBN 0-596-10214-3




                     @ramalhoorg
O padrão
Iterator permite
acessar os itens
de uma coleção
sequencialmente,
isolando o cliente
da implementação
da coleção.
Head First
Design Patterns
Poster
O'Reilly,
ISBN 0-596-10214-3




                     @ramalhoorg
Trem
                      class Trem(object):
                          def __init__(self, num_vagoes):
                              self.num_vagoes = num_vagoes
                          def __iter__(self):

 com                          return IteradorTrem(self.num_vagoes)

                      class IteradorTrem(object):


 iterator
                          def __init__(self, num_vagoes):
                              self.atual = 0
                              self.ultimo_vagao = num_vagoes - 1
                          def next(self):
                              if self.atual <= self.ultimo_vagao:
                                  self.atual += 1
                                  return 'vagao #%s' % (self.atual)
                              else:
      iter(t)                     raise StopIteration()

                         •   for vagao in t:

>>> t = Trem(4)
>>> for vagao in t:
                             •   invoca iter(t)
...   print(vagao)
vagao #1
                                 •   devolve IteradorTrem
vagao #2
vagao #3                     •   invoca itrem.next() até que
vagao #4                         ele levante StopIteration @ramalhoorg
Tômbola iterável
• Permitir iteração
  sobre tômbola em
  um laço for,
  devolvendo os itens
  em uma ordem
  sorteada

  Agora o exemplo
  faz mais sentido!
Recapitulando: iterável é...
• Um objeto a partir do qual a função iter
  consegue obter um iterador.
• A chamada iter(x):        interface Iterable

 • invoca x.__iter__() para obter um iterador
 • ou, se x.__iter__ não existe:
   • fabrica um iterador que acessa os itens de x
      sequenciamente fazendo x[0], x[1], x[2] etc.
           protocolo de sequência            @ramalhoorg
Iteração em C (exemplo 2)
 #include <stdio.h>

 int main(int argc, char *argv[]) {
     int i;
     for(i = 0; i < argc; i++)
         printf("%d : %sn", i, argv[i]);
     return 0;
 }
                      $   ./args2 alfa bravo charlie
                      0   : ./args2
                      1   : alfa
                      2   : bravo
                      3   : charlie          @ramalhoorg
Iteração em Python (ex. 2)
 import sys

 for i, arg in enumerate(sys.argv):
     print i, ':', arg



              $   python args2.py alfa bravo charlie
              0   : args2.py
              1   : alfa
              2   : bravo
              3   : charlie                 @ramalhoorg
Iterator x generator
• Gerador é uma generalização do iterador
• Por definição, um objeto iterador produz itens
  iterando sobre outro objeto (alguma coleção)
• Um gerador é um iterável que produz itens sem
  necessariamente acessar uma coleção
 • ele pode iterar sobre outro objeto mas também
    pode gerar itens por contra própria, sem
    qualquer dependência externa (ex. Fibonacci)
                                            @ramalhoorg
Função               >>> def g123():


geradora
                     ...     yield 1
                     ...     yield 2
                     ...     yield 3
                     ...
                     >>> for i in g123(): print i
                     ...

• Quando invocada,
                     1
                     2
                     3
  devolve um         >>> g = g123()
                     >>> g
  objeto gerador     <generator object g123 at 0x10e385e10>

• O objeto gerador
                     >>> g.next()
                     1
                     >>> g.next()
  é um iterável      2
                     >>> g.next()
                     3
                     >>> g.next()
                     Traceback (most recent call last):
                       File "<stdin>", line 1, in <module>
                     StopIteration                @ramalhoorg
Trem c/ função geradora
                      class Trem(object):
                          def __init__(self, num_vagoes):
                              self.num_vagoes = num_vagoes
                          def __iter__(self):
                              for i in range(self.num_vagoes):
                                  yield 'vagao #%s' % (i+1)
      iter(t)
                        •   for vagao in t:

>>> t = Trem(4)
>>> for vagao in t:
                            •   invoca iter(t)
...   print(vagao)
vagao #1
                                •   devolve gerador
vagao #2
vagao #3                    •   invoca gerador.next() até que
vagao #4                        ele levante StopIteration @ramalhoorg
class Trem(object):

Iterador        def __init__(self, num_vagoes):
                    self.num_vagoes = num_vagoes
                def __iter__(self):


clássico
                    return IteradorTrem(self.num_vagoes)

            class IteradorTrem(object):
                def __init__(self, num_vagoes):
12 linhas           self.atual = 0
                    self.ultimo_vagao = num_vagoes - 1
de código       def next(self):
                    if self.atual <= self.ultimo_vagao:
                        self.atual += 1
                        return 'vagao #%s' % (self.atual)
                    else:
                        raise StopIteration()

            mesma funcionalidade e desempenho!
Função           class Trem(object):
                     def __init__(self, num_vagoes):

geradora                 self.num_vagoes = num_vagoes
                     def __iter__(self):
                         for i in range(self.num_vagoes):
                             yield 'vagao #%s' % (i+1)
 3 linhas
Expressão
geradora             >>> g = (n for n in [1, 2, 3])
                     >>> for i in g: print i
                     ...
                     1
                     2
                     3

• Quando avaliada,   >>> g = (n for n in [1, 2, 3])
                     >>> g
  devolve um         <generator object <genexpr> at
                     0x109a4deb0>
  objeto gerador     >>> g.next()
                     1

• O objeto gerador   >>> g.next()
                     2
  é um iterável      >>> g.next()
                     3
                     >>> g.next()
                     Traceback (most recent call last):
                       File "<stdin>", line 1, in <module>
                     StopIteration

                                                 @ramalhoorg
Trem c/ expressão geradora
                      class Trem(object):
                          def __init__(self, num_vagoes):
                              self.num_vagoes = num_vagoes
                          def __iter__(self):
                              return ('vagao #%s' % (i+1)
                                      for i in range(self.num_vagoes))
      iter(t)
                           •   for vagao in t:

>>> t = Trem(4)
>>> for vagao in t:
                               •   invoca iter(t)
...   print(vagao)
vagao #1
                                   •   devolve gerador
vagao #2
vagao #3                       •   invoca gerador.next() até que
vagao #4                           ele levante StopIteration @ramalhoorg
Construtores embutidos
que consomem e
produzem iteráveis
• dict        • reversed
• enumerate   • set
• frozenset   • tuple
• list
                           @ramalhoorg
Módulo itertools
• geradores (potencialmente) infinitos
 • count(), cycle(), repeat()
• geradores que combinam vários iteráveis
 • chain(), tee(), izip(), imap(), product(), compress()...
• geradores que selecionam ou agrupam itens:
 • compress(), dropwhile(), groupby(), ifilter(), islice()...
• Iteradores que produzem combinações
 • product(), permutations(), combinations()...    @ramalhoorg
Exemplo prático com
funções geradoras
• Funções geradoras para desacoplar laços de
  leitura e escrita em uma ferramenta para
  conversão de bases de dados semi-estruturadas


https://github.com/ramalho/isis2json


                                           @ramalhoorg
Laço principal escreve
arquivo JSON




                         @ramalhoorg
Um outro laço lê os
registros a converter




                        @ramalhoorg
Implementação possível:
o mesmo laço lê e grava




                      @ramalhoorg
Mas e a lógica para ler
outro formato?




                          @ramalhoorg
Funções
do script
 • iterMstRecords*
 • iterIsoRecords*
 • writeJsonArray
 • main	

      * funções geradoras
                            @ramalhoorg
Função main:
leitura dos
argumentos




               @ramalhoorg
Função main: seleção
do formato escolha dadepende geradora
               de leitura
                          função
                                 do
de entrada     formato de entrada




                    função geradora escolhida
                    é passada como argumento
                                      @ramalhoorg
writeJsonArray:
escrever
registros
em JSON



                  @ramalhoorg
writeJsonArray:
itera sobre umas das
funções geradoras




                       @ramalhoorg
iterIsoRecords:
ler registros     função geradora!


de arquivo
ISO-2709



                            @ramalhoorg
iterIsoRecords
                        cria um novo dict
                        a cada iteração




                 produz (yield) registro
                 na forma de um dict @ramalhoorg
iterMstRecords:   função geradora!

ler registros
de arquivo
ISIS .MST



                             @ramalhoorg
iterMstRecords
iterIsoRecords
                    cria um novo dict
                    a cada iteração




             produz (yield) registro
             na forma de um dict @ramalhoorg
Geradores na prática




                       @ramalhoorg
Geradores na prática




                       @ramalhoorg
Geradores na prática




                       @ramalhoorg
Oficinas Turing:
    computação para programadores

•   Próximos lançamentos:

    •   4ª turma de Python para quem sabe Python

    •   3ª turma de Objetos Pythonicos

    •   1ª turma de Aprenda Python com um Pythonista



        Turing.com.br

Objetos Pythonicos - compacto

  • 1.
    Luciano Ramalho luciano@ramalho.org outubro/2012 ac to mp Objetos Pythonicos co Orientação a objetos e padrões de projeto em Python
  • 2.
    Conceito: “objeto” • Umcomponente de software que inclui dados (campos) 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
  • 3.
    Terminologia pythonica • Objetos possuematributos • Os atributos de um objeto podem ser: • métodos funções associadas • atributos de dados “campos” Figuras: bycicle (bicicleta), The Java Tutorial http://docs.oracle.com/javase/tutorial/java/concepts/object.html
  • 4.
    Exemplo: um objetodict >>> 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.
  • 5.
    Exemplo: um objetodict • dir revela atributos de um dict • atributos de dados e métodos >>> 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']
  • 6.
    Exemplo: um objetodict • Sobrecarga de operadores: • [ ]: __getitem__, __setitem__ >>> d {'AM': 'Manaus', 'PE': 'Recife'} >>> 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'}
  • 7.
    Exemplo: um objetodict • Atributos de dados: __class__, __doc__ >>> d.__class__ <type 'dict'> >>> type(d) <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)
  • 8.
    Exemplo: outro dict >>>d = dict() >>> d.keys() [] • d = dict() >>> d.get('bla') >>> print d.get('bla') é o mesmo que None d = {} >>> d.get('spam') >>> print d.get('spam') None >>> d.pop('ovos') Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'ovos' >>> d {} >>> len(d) 0 >>> d.__len__() 0
  • 9.
    Exercício: construir e controlar Tkinter.Label import Tkinter relogio = Tkinter.Label() relogio.pack() relogio['font'] = 'Helvetica 120 bold' relogio['text'] = strftime('%H:%M:%S') from time import strftime def tic():     relogio['text'] = strftime('%H:%M:%S')      def tac(): Note: no Tkinter, atributos     relogio.after(100, tictac) de dados são acessados via [ ]: tac() relogio.mainloop() __getitem__ __setitem__
  • 10.
    Tudo é umobjeto • Não existem “tipos primitivos” como em Java • desde Python 2.2, dezembro de 2001 >>> 5 + 3 8 >>> 5 .__add__(3) 8 >>> type(5) <type 'int'>
  • 11.
    Tipos embutidos • Implementadosem C, por eficiência • Comportamentos fixos • Texto: str, unicode • Números: int, long, float, complex, bool • Coleções: list, tuple, dict, set, frozenset • etc.
  • 12.
    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',)
  • 13.
    Funções são objetos >>>fatorial.__code__.co_code '|x00x00dx01x00jx00x00ox05x00x01dx02x00Sx01|x00x00tx00x00| x00x00dx02x00x18x83x01x00x14S' >>> from dis import dis >>> dis(fatorial.__code__.co_code) 0 LOAD_FAST 0 (0) 3 LOAD_CONST 1 (1) 6 COMPARE_OP 0 (<) Bytecode da 9 JUMP_IF_FALSE 5 (to 17) 12 POP_TOP 13 LOAD_CONST 2 (2) >> 16 RETURN_VALUE 17 POP_TOP função fatorial 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 >>>
  • 14.
    Tipagem forte • Otipo de um objeto nunca muda • Raramente Python faz conversão automática de 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' >>>
  • 15.
    Tipagem dinâmica: variáveis nãotê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'
  • 16.
    Orientação a objetos: aorigem • Linguagem Simula 1967 • Noruega: Ole-Johan Dahl e Kristen Nygaard • objetos, classes, sub-classes • métodos virtuais (funções associadas a objetos específicos somente em tempo de execução, e não na compilação)
  • 17.
    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” (invocação de métodos), “late binding” (métodos virtuais)
  • 18.
  • 19.
  • 20.
    Duck typing • “Sevoa 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 • x implementa o método __mult__(n), para n inteiro
  • 21.
    Tipagem forte xfraca JavaScript (ECMAScript 5) • Tipagem forte x fraca refere-se a em Node.js 0.6 conversão automática de valores de tipos diferente. • Linguagens de tipagem fraca são muito > 10 + '9' '109' > 10 + '9' * 1 liberais na mistura entre tipos, e isso é 19 uma fonte de bugs. > '10' + 9 * 1 '109' Veja alguns resultados estranhos obtidos > 0 == '0' com JavaScript, que tem tipagem fraca. true > 0 == '' Em Python as três expressões acima geram true TypeError, e as três últimas resultam False. > '0' == '' Python tem tipagem forte. false
  • 22.
    Tipagem forte xfraca, 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
  • 23.
    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 combinação JavaScript PHP dinâmica dinâmica fraca fraca } perigosa: bugs sorrateiros
  • 24.
    Conversões automáticas • Pythonfaz 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' >>>
  • 25.
    Tipos são classes •Para criar um novo tipo de objeto, crie uma classe • A função type devolve a classe de um objeto >>> class Mamifero(object): ... pass ... >>> kingkong = Mamifero() >>> type(kingkong) <class '__main__.Mamifero'> >>> ClasseDoKingKong = type(kingkong) >>> dumbo = ClasseDoKingKong() >>> dumbo <__main__.Mamifero object at 0x10045dcd0> >>> type(dumbo) <class '__main__.Mamifero'>
  • 26.
    Conceito: “classe” • Umacategoria, 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
  • 27.
    Exemplar de cão: instânciada classe Cao >>> rex = Cao() instanciação
  • 28.
    instanciação >>> rex = Cao('Rex') >>> rex Classe Cao Cao('Rex') >>> print rex Rex >>> rex.qt_patas class Mamifero(object): 4     """lição de casa: implementar""" >>> rex.latir() Rex: Au! class Cao(Mamifero): >>> rex.latir(2)     qt_patas = 4     carnivoro = True Rex: Au! Au!     nervoso = False >>> rex.nervoso = True     def __init__(self, nome): >>> rex.latir(3)         self.nome = nome Rex: Au! Au! Au! Au! Au! Au!     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) oopy/exemplos/cao.py
  • 29.
    Como atributos são acessados •Ao buscar o.a (atributo a do objeto o da classe C), o interpretador Python faz o seguinte: • 1) acessa atributo a da instancia o; caso não exista... • 2) acessa atributo a da classe C de o (type(o) ou o.__class__); caso nao exista... • 3) busca o atributo a nas superclasses de C, conforme a MRO (method resolution order)
  • 30.
    Classe Cao emPython class Mamifero(object):     """lição de casa: implementar""" • __init__ é o class Cao(Mamifero):     qt_patas = 4 construtor, ou melhor,     carnivoro = True     nervoso = False o inicializador •     def __init__(self, nome):         self.nome = nome     def latir(self, vezes=1): self é o 1º parâmetro         # quando nervoso, late o dobro formal em todos os         vezes = vezes + (self.nervoso * vezes)         print self.nome + ':' + ' Au!' * vezes métodos de instância     def __str__(self):         return self.nome     def __repr__(self):         return 'Cao({0!r})'.format(self.nome) oopy/exemplos/cao.py
  • 31.
    >>> rex =Cao('Rex') >>> rex Classe Cao Cao('Rex') >>> print rex Rex >>> rex.qt_patas class Mamifero(object): 4     """lição de casa: implementar""" >>> rex.latir() Rex: Au! class Cao(Mamifero): invocação >>> rex.latir(2)     qt_patas = 4 Rex: Au! Au!     carnivoro = True     nervoso = False •     def __init__(self, nome):         self.nome = nome     def latir(self, vezes=1): na invocação do         # quando nervoso, late o dobro método, a instância         vezes = vezes + (self.nervoso * vezes)         print self.nome + ':' + ' Au!' * vezes é passada     def __str__(self):         return self.nome automaticamente     def __repr__(self):         return 'Cao({0!r})'.format(self.nome) na posição do self oopy/exemplos/cao.py
  • 32.
    Classe Cao emPython class Mamifero(object):     """lição de casa: implementar""" • atributos de dados na classe funcionam class Cao(Mamifero):     qt_patas = 4 como valores default     carnivoro = True     nervoso = False para os atributos das     def __init__(self, nome):         self.nome = nome instâncas •     def latir(self, vezes=1):         # quando nervoso, late o dobro         vezes = vezes + (self.nervoso * vezes) atributos da instância         print self.nome + ':' + ' Au!' * vezes só podem ser     def __str__(self):         return self.nome acessados via self     def __repr__(self):         return 'Cao({0!r})'.format(self.nome) oopy/exemplos/cao.py
  • 33.
    Mamifero: superclasse deCao class Mamifero(object): UML diagrama de classe     """lição de casa: implementar""" especialização class Cao(Mamifero): generalização     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) oopy/exemplos/cao.py
  • 34.
    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 Diz a lenda que o cão     def servir(self): São Bernardo leva um         if self.doses == 0: pequeno barril de conhaque             raise ValueError('Acabou o conhaque!')         self.doses -= 1 para resgatar viajantes         msg = '{0} serve o conhaque (restam {1} doses)' perdidos na neve.         print msg.format(self.nome, self.doses)
  • 35.
    Subclasses >>> sansao = SaoBernardo('Sansao') de Cao >>> sansao.servir() Sansao serve o conhaque (restam 9 doses) >>> sansao.doses = 1 >>> sansao.servir() Sansao serve o conhaque (restam 0 doses) >>> sansao.servir() class Pequines(Cao): Traceback (most recent call last):     nervoso = True ...      class Mastiff(Cao): ValueError: Acabou o conhaque!     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)
  • 36.
    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)
  • 37.
    Interface • Interface é um conceito essencial em OO • não depende de uma palavra reservada A interface fornece uma separação entre a implementação de uma abstração e seus clientes. Ela limita os detalhes de implementação que os clientes podem ver. Também especifica a funcionalidade que as implementações devem prover. P. S. Canning, W. R. Cook, W. L. Hill, and W. G. Olthoff. 1989. Interfaces for strongly-typed object- oriented programming. In Conference proceedings on Object-oriented programming systems, languages and applications (OOPSLA '89). ACM, New York, NY, USA, 457-467. DOI=10.1145/74877.74924 http://doi.acm.org/10.1145/74877.74924
  • 38.
    Interfaces e protocolos •Em SmallTalk, as interfaces eram chamadas de “protocolos”. • Não há verificação de interfaces na linguagem, mas algumas IDEs (“browsers”) permitem agrupar os métodos por protocolo para facilitar a leitura • Um protocolo é uma interface informal, não declarada porém implementada por métodos concretos
  • 39.
    Interfaces em Python •Conceitualmente, sempre existiram como protocolos • Não havia maneira formal de especificar interfaces em Python até a versão 2.5 • usava-se termos como “uma sequência” ou “a file-like object” • Agora temos ABC (Abstract Base Class) • com herança múltipla, como em C++
  • 40.
    Exemplo: tômbola • Sortearum a um todos os itens de uma coleção finita, sem repetir • A mesma lógica é usada em sistemas para gerenciar banners online
  • 41.
    Interface da tômbola •Carregar itens • Misturar itens • Sortear um item • Indicar se há mais itens
  • 42.
    Projeto da tômbola •UML: diagrama de classe
  • 43.
    TDD: Test DrivenDesign • Metodologia de desenvolvimento iterativa na qual, para cada funcionalidade nova, um teste é criado antes do código a ser implementado • Esta inversão ajuda o programador a desenvolver com disciplina apenas uma funcionalidade de cada vez, mantendo o foco no teste que precisa passar • Cada iteração de teste/implementação deve ser pequena e simples: “baby steps” (passinhos de bebê)
  • 44.
    Doctests • Um dosmódulos para fazer testes automatizados na biblioteca padrão de Python • o outro módulo é o unittest, da família xUnit • Doctests foram criados para testar exemplos embutidos na documentação • Usaremos doctests para especificar exercícios • Exemplo: $ python -m doctest cao.rst oopy/exemplos/cao.rst
  • 45.
    Coding Dojo • Implementaçãoda classe Tombola, com testes feitos em Doctest
  • 46.
    Implementação da tômbola # coding: utf-8 import random class Tombola(object): itens = None def carregar(self, itens): self.itens = list(itens) def carregada(self): return bool(self.itens) def misturar(self): • Python 2.2 a 2.7 random.shuffle(self.itens) def sortear(self): return self.itens.pop()
  • 47.
    Iteração em Ce Python #include <stdio.h> int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) printf("%sn", argv[i]); return 0; } import sys for arg in sys.argv: print arg @ramalhoorg
  • 48.
    Iteração em Java classArgumentos { public static void main(String[] args) { for (int i=0; i < args.length; i++) System.out.println(args[i]); } } $ java Argumentos alfa bravo charlie alfa bravo charlie @ramalhoorg
  • 49.
    ano: Iteração em Java≥1.5 2004 • Enhanced for (for melhorado) class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); } } $ java Argumentos2 alfa bravo charlie alfa bravo charlie @ramalhoorg
  • 50.
    ano: Iteração em Java≥1.5 2004 • Enhanced for (for melhorado) class Argumentos2 { public static void main(String[] args) { for (String arg : args) System.out.println(arg); } } ano: import sys 1991 for arg in sys.argv: print arg @ramalhoorg
  • 51.
    Exemplos de iteração •Iteração em Python não se limita a tipos primitivos • Exemplos • string • arquivo • Django QuerySet • Baralho (em: “OO em Python sem Sotaque”) https://slideshare.net/ramalho/ @ramalhoorg
  • 52.
    List comprehension List comprehensions ● Compreensão de lista ou abrangência ● Exemplo: usar todos os elementos: • Expressões que consomem L2 = [n*10 for n in L] – iteráveis e produzem listas qualquer iterável resultado: uma lista >>> s = 'abracadabra' >>> l = [ord(c) for c in s] >>> [ord(c) for c in s] [97, 98, 114, 97, 99, 97, 100, 97, 98, 114, 97] @ramalhoorg
  • 53.
    Set & dictcomprehensions • Expressões que consomem iteráveis e produzem sets ou dicts >>> s = 'abracadabra' >>> {c for c in s} set(['a', 'r', 'b', 'c', 'd']) >>> {c:ord(c) for c in s} {'a': 97, 'r': 114, 'b': 98, 'c': 99, 'd': 100} @ramalhoorg
  • 54.
    Em Python ocomando for itera sobre... “iteráveis” • Definicão preliminar informal: • “iterável” = que pode ser iterado • assim como: “desmontável” = que pode ser desmontado • Iteráveis podem ser usados em outros contextos além do laço for @ramalhoorg
  • 55.
    Tipos iteráveis embutidos • basestring • frozenset • str • list • unicode • set • dict • tuple • file • xrange @ramalhoorg
  • 56.
    Funções embutidas que consomemiteráveis • all • max • any • min • filter • reduce • iter • sorted • len • sum • map • zip @ramalhoorg
  • 57.
    Operações com iteráveis >>> a, b, c = 'XYZ' • Desempacotamento >>> 'X' a de tupla >>> b 'Y' • em atribuições >>> 'Z' >>> c g = (n for n in [1, 2, 3]) • em chamadas de funções >>> >>> a, b, c = g a >>> def soma(a, b): 1 ... return a + b >>> b ... 2 >>> soma(1, 2) >>> c 3 3 >>> t = (3, 4) >>> soma(t) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: soma() takes exactly 2 arguments (1 given) >>> soma(*t) @ramalhoorg 7
  • 58.
    Em Python, umiterável é... • Um objeto a partir do qual a função iter consegue obter um iterador. • A chamada iter(x): • invoca x.__iter__() para obter um iterador • ou, se x.__iter__ não existe: • fabrica um iterador que acessa os itens de x sequenciamente fazendo x[0], x[1], x[2] etc. @ramalhoorg
  • 59.
    Protocolo de sequência >>> t = Trem(4) >>> len(t) 4 __len__ >>> t[0] 'vagao #1' >>> t[3] __getitem__ 'vagao #4' >>> t[-1] 'vagao #4' >>> for vagao in t: ... print(vagao) __getitem__ vagao #1 vagao #2 vagao #3 vagao #4 @ramalhoorg
  • 60.
    Protocolo desequência • implementação “informal” da interface class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __len__(self): return self.num_vagoes def __getitem__(self, pos): indice = pos if pos >= 0 else self.num_vagoes + pos if 0 <= indice < self.num_vagoes: # indice 2 -> vagao #3 return 'vagao #%s' % (indice+1) else: @ramalhoorg raise IndexError('vagao inexistente %s' % pos)
  • 61.
    Tômbola como sequência •Permitir contagem de itens • Permitir acesso direto a cada item pela ordem em que foi colocado Sim, este é um exemplo forçado... mas daqui a pouco melhora!
  • 62.
    Interface Sequence • collections.Sequence from collections import Sequence class Trem(Sequence): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __len__(self): return self.num_vagoes def __getitem__(self, pos): indice = pos if pos >= 0 else self.num_vagoes + pos if 0 <= indice < self.num_vagoes: # indice 2 -> vagao #3 return 'vagao #%s' % (indice+1) else: @ramalhoorg raise IndexError('vagao inexistente %s' % pos)
  • 63.
    Herança de Sequence >>>t = Trem(4) >>> 'vagao #2' in t True >>> 'vagao #5' in t False >>> for i in reversed(t): print i ... vagao #4 vagao #3 from collections import Seque vagao #2 vagao #1 class Trem(Sequence): >>> t.index('vagao #2') def __init__(self, num_va 1 self.num_vagoes = num >>> t.index('vagao #7') def __len__(self): Traceback (most recent call last): return self.num_vagoe ... def __getitem__(self, pos ValueError @ramalhoorg indice = pos if pos >
  • 64.
    Interface Iterable • Iterable provêum método __iter__ • O método __iter__ devolve uma instância de Iterator @ramalhoorg
  • 65.
    Iterator é... • umpadrão de projeto Design Patterns Gamma, Helm, Johnson & Vlissides Addison-Wesley, ISBN 0-201-63361-2 @ramalhoorg
  • 66.
  • 67.
    O padrão Iterator permite acessaros itens de uma coleção sequencialmente, isolando o cliente da implementação da coleção. Head First Design Patterns Poster O'Reilly, ISBN 0-596-10214-3 @ramalhoorg
  • 68.
    Trem class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): com return IteradorTrem(self.num_vagoes) class IteradorTrem(object): iterator def __init__(self, num_vagoes): self.atual = 0 self.ultimo_vagao = num_vagoes - 1 def next(self): if self.atual <= self.ultimo_vagao: self.atual += 1 return 'vagao #%s' % (self.atual) else: iter(t) raise StopIteration() • for vagao in t: >>> t = Trem(4) >>> for vagao in t: • invoca iter(t) ... print(vagao) vagao #1 • devolve IteradorTrem vagao #2 vagao #3 • invoca itrem.next() até que vagao #4 ele levante StopIteration @ramalhoorg
  • 69.
    Tômbola iterável • Permitiriteração sobre tômbola em um laço for, devolvendo os itens em uma ordem sorteada Agora o exemplo faz mais sentido!
  • 70.
    Recapitulando: iterável é... •Um objeto a partir do qual a função iter consegue obter um iterador. • A chamada iter(x): interface Iterable • invoca x.__iter__() para obter um iterador • ou, se x.__iter__ não existe: • fabrica um iterador que acessa os itens de x sequenciamente fazendo x[0], x[1], x[2] etc. protocolo de sequência @ramalhoorg
  • 71.
    Iteração em C(exemplo 2) #include <stdio.h> int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) printf("%d : %sn", i, argv[i]); return 0; } $ ./args2 alfa bravo charlie 0 : ./args2 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
  • 72.
    Iteração em Python(ex. 2) import sys for i, arg in enumerate(sys.argv): print i, ':', arg $ python args2.py alfa bravo charlie 0 : args2.py 1 : alfa 2 : bravo 3 : charlie @ramalhoorg
  • 73.
    Iterator x generator •Gerador é uma generalização do iterador • Por definição, um objeto iterador produz itens iterando sobre outro objeto (alguma coleção) • Um gerador é um iterável que produz itens sem necessariamente acessar uma coleção • ele pode iterar sobre outro objeto mas também pode gerar itens por contra própria, sem qualquer dependência externa (ex. Fibonacci) @ramalhoorg
  • 74.
    Função >>> def g123(): geradora ... yield 1 ... yield 2 ... yield 3 ... >>> for i in g123(): print i ... • Quando invocada, 1 2 3 devolve um >>> g = g123() >>> g objeto gerador <generator object g123 at 0x10e385e10> • O objeto gerador >>> g.next() 1 >>> g.next() é um iterável 2 >>> g.next() 3 >>> g.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration @ramalhoorg
  • 75.
    Trem c/ funçãogeradora class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): for i in range(self.num_vagoes): yield 'vagao #%s' % (i+1) iter(t) • for vagao in t: >>> t = Trem(4) >>> for vagao in t: • invoca iter(t) ... print(vagao) vagao #1 • devolve gerador vagao #2 vagao #3 • invoca gerador.next() até que vagao #4 ele levante StopIteration @ramalhoorg
  • 76.
    class Trem(object): Iterador def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): clássico return IteradorTrem(self.num_vagoes) class IteradorTrem(object): def __init__(self, num_vagoes): 12 linhas self.atual = 0 self.ultimo_vagao = num_vagoes - 1 de código def next(self): if self.atual <= self.ultimo_vagao: self.atual += 1 return 'vagao #%s' % (self.atual) else: raise StopIteration() mesma funcionalidade e desempenho! Função class Trem(object): def __init__(self, num_vagoes): geradora self.num_vagoes = num_vagoes def __iter__(self): for i in range(self.num_vagoes): yield 'vagao #%s' % (i+1) 3 linhas
  • 77.
    Expressão geradora >>> g = (n for n in [1, 2, 3]) >>> for i in g: print i ... 1 2 3 • Quando avaliada, >>> g = (n for n in [1, 2, 3]) >>> g devolve um <generator object <genexpr> at 0x109a4deb0> objeto gerador >>> g.next() 1 • O objeto gerador >>> g.next() 2 é um iterável >>> g.next() 3 >>> g.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration @ramalhoorg
  • 78.
    Trem c/ expressãogeradora class Trem(object): def __init__(self, num_vagoes): self.num_vagoes = num_vagoes def __iter__(self): return ('vagao #%s' % (i+1) for i in range(self.num_vagoes)) iter(t) • for vagao in t: >>> t = Trem(4) >>> for vagao in t: • invoca iter(t) ... print(vagao) vagao #1 • devolve gerador vagao #2 vagao #3 • invoca gerador.next() até que vagao #4 ele levante StopIteration @ramalhoorg
  • 79.
    Construtores embutidos que consomeme produzem iteráveis • dict • reversed • enumerate • set • frozenset • tuple • list @ramalhoorg
  • 80.
    Módulo itertools • geradores(potencialmente) infinitos • count(), cycle(), repeat() • geradores que combinam vários iteráveis • chain(), tee(), izip(), imap(), product(), compress()... • geradores que selecionam ou agrupam itens: • compress(), dropwhile(), groupby(), ifilter(), islice()... • Iteradores que produzem combinações • product(), permutations(), combinations()... @ramalhoorg
  • 81.
    Exemplo prático com funçõesgeradoras • Funções geradoras para desacoplar laços de leitura e escrita em uma ferramenta para conversão de bases de dados semi-estruturadas https://github.com/ramalho/isis2json @ramalhoorg
  • 82.
  • 83.
    Um outro laçolê os registros a converter @ramalhoorg
  • 84.
    Implementação possível: o mesmolaço lê e grava @ramalhoorg
  • 85.
    Mas e alógica para ler outro formato? @ramalhoorg
  • 86.
    Funções do script •iterMstRecords* • iterIsoRecords* • writeJsonArray • main * funções geradoras @ramalhoorg
  • 87.
  • 88.
    Função main: seleção doformato escolha dadepende geradora de leitura função do de entrada formato de entrada função geradora escolhida é passada como argumento @ramalhoorg
  • 89.
  • 90.
    writeJsonArray: itera sobre umasdas funções geradoras @ramalhoorg
  • 91.
    iterIsoRecords: ler registros função geradora! de arquivo ISO-2709 @ramalhoorg
  • 92.
    iterIsoRecords cria um novo dict a cada iteração produz (yield) registro na forma de um dict @ramalhoorg
  • 93.
    iterMstRecords: função geradora! ler registros de arquivo ISIS .MST @ramalhoorg
  • 94.
    iterMstRecords iterIsoRecords cria um novo dict a cada iteração produz (yield) registro na forma de um dict @ramalhoorg
  • 95.
  • 96.
  • 97.
  • 98.
    Oficinas Turing: computação para programadores • Próximos lançamentos: • 4ª turma de Python para quem sabe Python • 3ª turma de Objetos Pythonicos • 1ª turma de Aprenda Python com um Pythonista Turing.com.br