About

Swaroop C H is 27 years of age. He graduated in B.E. (Computer Science) from PESIT, Bangalore, India. He has previously worked at Yahoo! and Adobe.

More about

Page
Personal tools
COLLECTION
Collection

Python pt-br:Programacao Orientada a Objetos

From Notes

Jump to: navigation, search

Contents

[edit] Introdução

Em todos os programas que escrevemos até agora, projetamos nosso programa em termos de funções, isto é, blocos de declarações que manipulam dados. Esta é a chamada maneira de programar orientada a procedimentos. Existe outra maneira de organizar seu programa que combina dados e funcionalidades e as empacota dentro de algo chamado objeto. Este é o chamado paradigma de programação orientada a objetos. Na maioria das vezes você pode usar a programação orientada a procedimentos, mas quando estamos escrevendo grandes programas ou temos um problema que se adequa melhor a esse método, você pode usar técnicas de programação orientadas a objetos.

Classes e objetos são os dois principais aspectos da programação orientada a objetos. Uma classe cria um novo tipo onde objetos são instâncias da classe. Uma analogia é que você pode ter variáveis do tipo int o que significa dizer que variáveis que armazenam inteiros são variáveis que são instâncias (objetos) da classe int.

Nota para Programadores de Linguagens Estáticas
Note que mesmo os inteiros são tratados como objetos (da classe int). Isso é diferente do C++ e do Java (antes da versão 1.5) onde os inteiros são tipos primitivos nativos. Veja help(int) para mais detalhes sobre a classe.
Programadores C# e Java 1.5 encontrarão semelhanças com os conceitos de boxing e unboxing.

Objetos podem armazenar dados utilizando variáveis comuns que pertencem ao objeto. Variáveis que pertencem a um objeto o classe são chamadas de campos. Objetos também podem ter funcionalidade por meio de funções que pertencem a uma classe. Tais funções são chamadas de métodos da classe. Esta terminologia é importante porque nos ajuda a diferencias entre funções e variáveis que são independentes e as que pertencem a uma classe ou objeto. Coletivamente, os campos ou métodos podem ser considerados atributos da classe.

Os campos podem ser de dois tipos - eles podem pertencer a cada instância/objeto ou podem pertencer à classe em si. Elas são chamadas de variáveis de instância e variáveis de classe, respectivamente.

Uma classe é criada usando a palavra-chave class. Os campos e métodos da classe são listados em um bloco indentado.

[edit] O self

Os métodos tem apenas uma diferença específica em relação a funções comuns: eles devem ter um primeiro nome extra que deve ser incluído no início da lista de parâmetros, mas você não deve atribuir um valor a esse parâmetro quando chama o método, pois o Python o fornece implicitamente. Essa variável em particular refere-se ao próprio objeto e, por convenção, é chamada de self.

Embora seja possível dar qualquer nome a este parâmetro, é fortemente recomendado que você use o nome self. Existem muitas vantagens um usar um nome padrão - qualquer leitor do seu programa irá reconhecê-lo imediatamente e mesmo IDEs especializadas podem ajudá-lo se você usar self.

Nota para Programadores C++/Java/C#
O self em Python é equivalente ao ponteiro this em C++ e à referência this em Java e C#.

Você deve estar intrigado sobre como Python atribui o valor para self e o porquê você não precisa atribuir um valor para ele. Um exemplo tornará mais claro. Digamos que você tenha uma classe chamada MyClass e uma instância desta classe chamada MyObject. Quando você invoca um método deste objeto como MyObject.method(arg1, arg2), isto é convertido automaticamente pelo Python em MyClass.method(MyObject, arg1, arg2) - este é o sentido especial de self.

Isto também significa que, se você tem um método que não possui argumentos, então você ainda assim deverá ter um argumento - o self.

[edit] Classes

A classe mais simples possível é mostrada no seguinte exemplo.

#!/usr/bin/python
# Arquivo: simplestclass.py
 
class Person:
    pass # Um bloco vazio
 
p = Person()
print(p)

Saída:

   $ python simplestclass.py
   <__main__.Person object at 0x019F85F0>

Como Funciona:

Criamos uma nova classe usando a instrução class e o nome da classe. Na sequência, temos um bloco de instruções que formam o corpo da classe. Neste caso, temos um bloco vazio que é indicado pela instrução pass.

Em seguida, criamos um objeto/instância desta classe usando o nome da classe seguido por um par de parênteses. (Aprenderemos mais sobre instanciação na próxima seção). Para verificação, confirmamos o tipo da variável simplementes imprimindo-a (print). Isso nos diz que temos uma instância da classe Person no módulo __main__.

Observe que o endereço de memória onde seu objeto está armazenado também é impresso. O endereço terá um valor diferente em seu computador, uma vez que o Python poderá armezenar o objeto onde quer que haja espaço.

[edit] Métodos de Objetos

Já discutimos que classes/objetos podem ter métodos exatamente como funções, exceto pelo fato de que temos uma variável extra - self. Vejamos um exemplo.

#!/usr/bin/python
# Arquivo: method.py
 
class Person:
    def sayHi(self):
        print('Hello, how are you?')
 
p = Person()
p.sayHi()
 
# Este pequeno exemplo também pode ser escrito como Person().sayHi()

Saída:

   $ python method.py
   Hello, how are you?

Como Funciona:

Aqui vemos o self em ação. Observe que o método sayHi não possui qualquer parâmetro mas, ainda assim, temos o self na definição do método.

[edit] O método __init__

Existem vários nomes de métodos que possuem significado especial em classes Python. Veremos agora o significado do método __init__.

O método __init__ é invocado pronta e automaticamente quando um objeto de uma classe é instanciado. Este método é útil para realizar quaisquer iniciações que você desejar fazer com o seu objeto. Observe os sublinhados duplos, no início e no final do nome.

Exemplo:

#!/usr/bin/python
# Arquivo: class_init.py
 
class Person:
    def __init__(self, name):
        self.name = name
    def sayHi(self):
        print('Hello, my name is', self.name)
 
p = Person('Swaroop')
p.sayHi()
 
# Este exemplo também pode ser escrito como Person('Swaroop').sayHi()

Saída:

   $ python class_init.py
   Hello, my name is Swaroop

Como Funciona:

Aqui, definimos o método __init__ com um parâmetro name (além do usual parâmetro self). Então, simplesmente criamos um novo atributo também chamado name. Observe que tratam-se de variáveis diferentes, embora tenham o mesmo nome ('name'). A notação por ponto nos permite diferenciar uma variável da outra.

Mais importante ainda, observe que nós não invocamos o método __init__ explicitamente, mas apenas passamos os argumentos entre os parênteses após o nome da classe, quando criamos uma nova instância. Este é o significado especial deste método.

Agora, podemos usar o atributo self.name em nossos métodos, como é demonstrado no método sayHi.

Nota para Programadores C++/Java/C#
O método __init__ é análogo a um construtor em C++, Java ou C#.

[edit] Variáveis de Classe e Objeto

Já discutimos a respeito do lado funcional de classes e objetos (i.e. métodos), agora vamos aprender sobre dados. Dados (i.e. campos ou atributos) são nada mais que variáveis comuns que estão associadas ao espaço de nomes (namespace) de uma classe e/ou objeto. Isto significa que esses nomes são válidos apenas no contexto de uma classe e/ou objeto particular. Por isso são chamados de espaços de nomes (name spaces).

Existem dois tipos de atributos (ou campos) - variáveis de classe e variáveis de objeto, que são classificados dependendo de quem - a classe ou o objeto - possui essas variáveis.

Variáveis de classe são compartilhadas - elas podem ser acessadas por todas as instâncias daquela classe. Há apenas uma cópia de uma variável de classe e quando qualquer instância daquela classe modifica o seu valor, essa mudança será vista por todas as outras instâncias.

Variáveis de objeto são particulares a cada instância individual de uma classe. Neste caso, cada objeto possui a sua própria cópia, ou seja, elas não são compartilhadas e não estão relacionadas de qualquer maneira à outras variáveis com o mesmo em outras instâncias. Um exemplo tornará mais fácil de entender:

#!/usr/bin/python
# Arquivo: objvar.py
 
class Robot:
    '''Representa um robô com um nome.'''
 
    # Uma variável de classe, para contar o número de robôs.
    population = 0
 
    def __init__(self, name):
        '''Inicia os dados.'''
        self.name = name
        print('(Iniciando {0})'.format(self.name))
 
        # Quando este robô é criado ele é contabilizado
        # na população geral de robôs
        Robot.population += 1
 
    def __del__(self):
        '''Estou morrendo.'''
        print('{0} sendo destruído!'.format(self.name))
 
        Robot.population -= 1
 
        if Robot.population == 0:
            print('{0} era o último.'.format(self.name))
        else:
            print('Existe(m) ainda {0:d} robô(s) trabalhando.'.format(Robot.population))
 
    def sayHi(self):
        '''Saudações do robô.
 
        Sim, eles podem fazer isso.'''
        print('Saudações, meus mestres me chamam {0}.'.format(self.name))
 
    def howMany(klass):
        '''Imprime a população atual.'''
        print('Temos um total de {0:d} robô(s).'.format(Robot.population))
    howMany = classmethod(howMany)
 
droid1 = Robot('R2-D2')
droid1.sayHi()
Robot.howMany()
 
droid2 = Robot('C-3PO')
droid2.sayHi()
Robot.howMany()
 
print("\nRobôs podem realizar algum trabalho aqui.\n")
 
print("Robôs terminaram seus trabalhos. Então vamos destruí-los.")
del droid1
del droid2
 
Robot.howMany()

Saída:

   (Iniciando R2-D2)
   Saudações, meus mestres me chamam R2-D2.
   Temos um total de 1 robô(s).
   (Iniciando C-3PO)
   Saudações, meus mestres me chamam C-3PO.
   Temos um total de 2 robô(s).
   
   Robôs podem realizar algum trabalho aqui.
   
   Robôs terminaram seus trabalhos. Então vamos destruí-los.
   R2-D2 sendo destruído!
   Existe(m) ainda 1 robô(s) trabalhando.
   C-3PO sendo destruído!
   C-3PO era o último.
   Temos um total de 0 robô(s).

Como Funciona:

Este é um longo exemplo, mas ajuda a demonstrar a natureza das variáveis de classe e de instância. Aqui, population pertence à classe Robot e, portanto, é uma variável de classe. A variável name pertence ao objeto (instância, pois é atribuída usando self) e, portanto, é uma variável de instância.

Assim, nos referímos à variável de classe population como Robot.population e não como self.population. Por outro lado, nos referímos à variável de instância name usando a notação self.name, nos métodos daquele objeto. Lembre-se desta simples diferença entre variáveis de classe e de instância. Note também que, uma variável de instância com o mesmo nome de uma variável de classe irá se sobrepor à variável de classe, escondendo-a!

O método howMany é, na verdade, um método de classe, e não um método de instância. Portanto, o primeiro argumento é klass. Note que escrevemos klass com 'k', por que não podemos reutilizar a palavra class que é a palavra-chave usada para criar nova classes.

Ainda não terminamos de definir 'howMany', pois temos que tornar explícito que trata-se de um método de classe. Para isso, empacotamos o método usando a função interna classmethod.

Podemos obter o mesmo efeito usando decorators:

    @classmethod
    def howMany(klass):
        '''Imprime a população atual.'''
        print('Temos um total de {0:d} robô(s).'.format(Robot.population))

Pense em decorators como sendo atalhos para uma chamada explícita a uma instrução, como vimos neste exemplo.

Note que o método __init__ é usado para iniciar a instância de Robot com um nome. Na iniciação, incrementamos em 1 a população (population) de robôs, uma vez que temos mais um robô adicionado. Observe também que os valores de self.name são específicos a cada objeto demonstrado a natureza das variáveis de instância.

Lembre-se que você deve referir-se às variáveis e métodos de uma mesma instância somente em conjunto com self. Isto é chamado de referência ao atributo.

Neste exemplo, também vemos o uso de docstrings para classes e também para métodos. Podemos acessar a documentação da classe em tempo de execução usando Robot.__doc__ e a documentação de um método usando Robot.sayHi.__doc__.

Assim como o método __init__, há um outro método especial __del__ que é invocado com quando um objeto está para ser eliminado, isto é, quando não será mais utilizado e será eliminado para que o sistema reutilize o trecho de memória que aquela instância estiver ocupando. Neste método, simplesmente decrementamos a população geral de robôs em 1 usando Person.population -= 1.

O método __del__ será executado quando o objeto não estiver mais em uso e não há garantias de quando o método será invocado. Se você deseja vê-lo em ação, devemos utilizar a instrução del explicitamente, que é exatamente o que fizemos.

Nota para Programadores C++/Java/C#
Todos os membros de classe (incluindo dados - variáveis) são públicos e todos os métodos são virtual em Python.
Há uma exceção: Se você definir nomes para os membros prefixados com dois caracteres de sublinha como em __privatevar, Python altera esse nome internamente (técnica chamada name-mangling) para tornar a variável, efetivamente, privada.
Assim, segue-se a convenção de que, qualquer variável que deva ser utilizada apenas pela classe ou pela instância deve iniciar com um sublinhado e, todos os outros nomes são públicos e podem ser utilizados por outras classes/objetos. Lembre-se de que esta é apenas uma convenção e não é exigido pelo Python (exceto para prefixos que iniciem com dois caracteres de sublinhado).
Note também que o método __del__ é análogo ao conceito de métodos destructor.

[edit] Herança

Um dos maiores benefícios da programação orientada a objetos é reusabilidade de código, e uma maneiras de obter isto é através do mecanismo de herança. Herança pode ser imaginada como a implementação de um relacionamento tipo e subtipo entre classes.

Suponha que você deva escrever um programa para controle de professores e alunos em uma escola. Eles possuem características comuns tais como nome, idade e endereço. Eles também possuem características específicas tais como salário, matérias e leaves para professores e notas e mensalidades para alunos.

Você pode criar duas classes independentes, uma para tipo, mas adicionar uma nova caraterística comum significará adicionar nas duas classes independentes. Isto torna-se rapidamente desajeitado.

Um modo mais eficiente é criar uma classe comum chamada SchoolMember e então fazer outras duas classes professor (Teacher) e aluno (Student) herdarem suas característias, isto é, elas serão seus sub-tipos, e então poderemos adicionar características específicas para cada sub-tipo.

Existem muitas vantagens nesta abordagem. Se adicionarmos ou mudarmos qualquer funcionalidade em SchoolMember, isto será automaticamente refletido em seus sub-tipos. Por exemplo, você poderá adicionar um novo campo ID para professores e alunos simpĺesmente adicionando esse campo na classe SchoolMember. Entretanto, mudanças em sub-tipos não afetarão outros sub-tipos. Outra vantagem é que você pode se referir aos objetos professores e alunos como objetos SchoolMember, o que pode ser útil em algumas situações, tal como contar o número total de membros. Isto é chamado de polimorfismo, onde um sub-tipo pode ser substituído em qualquer situação onde um super-tipo é esperado, isto é, o objeto pode ser manipulado como se fosse uma instância da classe pai.

Observe também que nós reutilizamos o código da classe pai (super-tipo) e por isso não precisamos repetí-lo nos sub-tipos, como teríamos que fazer no caso de usarmos classes independentes.

A classe SchoolMember nesta situação é conhecida como classe base (base class) ou super classe.. As classes Teacher e Student são chamadas de classes derivadas (derived classes) ou sub-classes.

Vamos implementar o exemplo citado:

#!/usr/bin/python
# Arquivo: inherit.py
 
class SchoolMember:
    '''Representa qualquer membro da escola.'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(Iniciado SchoolMember: {0})'.format(self.name))
 
    def tell(self):
        '''Imprime os detalhes desta instância.'''
        print('Nome:"{0}" Idade:"{1}"'.format(self.name, self.age), end=" ")
 
class Teacher(SchoolMember):
    '''Representa um professor.'''
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(Iniciado Teacher: {0})'.format(self.name))
 
    def tell(self):
        SchoolMember.tell(self)
        print('Salário: "{0:d}"'.format(self.salary))
 
class Student(SchoolMember):
    '''Representa um aluno.'''
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Iniciado Student: {0})'.format(self.name))
 
    def tell(self):
        SchoolMember.tell(self)
        print('Nota: "{0:d}"'.format(self.marks))
 
t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)
 
print() # imprime uma linha em branco
 
members = [t, s]
for member in members:
    member.tell() # funciona tanto para Teachers como para Students

Saída:

   $ python inherit.py
   (Iniciado SchoolMember: Mrs. Shrividya)
   (Iniciado Teacher: Mrs. Shrividya)
   (Iniciado SchoolMember: Swaroop)
   (Iniciado Student: Swaroop)
   
   Nome:"Mrs. Shrividya" Idade:"40" Salário: "30000"
   Nome:"Swaroop" Idade:"25" Nota: "75"

Como Funciona:

Para usar herança, especificamos os nomes das classes base em uma tupla, logo após o nome da classe em sua declaração. Em seguida, observamos que o método __init__ da classe base é explicitamente invocado usando a variável self, de tal maneira que podemos iniciar a porção da classe base do objeto. É muito importante lembrar-se disto - Python não invocará automaticamente o construtor da classe base, por isso você deverá invocá-lo explicitamente.

Observamos também que podemos invocar métodos da classe base prefixando o nome da classe na chamada do método e então passar como argumento a variável self além de quaisquer outros argumentos.

Note que podemos tratar instâncias de Teacher ou Student simplesmente como instâncias de SchoolMember, tal como quando usamos o método tell da classe SchoolMember.

Ainda, note que é invocado o método tell do sub-tipo e não o método tell da classe SchoolMember. Uma maneira de enteder isto é que Python sempre procura pelo método invocado, primeiro no tipo real do objeto, o que é verificado neste caso. Se o método invocado não puder ser encontrado, Python irá procurar pelo método nas classes base (especificadas na tupla onde a classe é declarada) uma-a-uma na ordem em que foram escritas.

Uma nota sobre terminologia - se mais de uma classe é fornecida na tupla de herança (na declaração da classe), isto é chamado de herança múltipla.

[edit] Metaclasses

Há muito mais no vasto tópico da programação orientada a objetos, mas tocaremos levemente em apenas alguns poucos tópicos apenas para ficarmos sabendo que existem.

Assim como usamos classes para criar objetos, podemos utilizar metaclasses para criar classes. Metaclasses são usadas para modificar ou criar novos comportamentos em classes.

Vamos tomar um exemplo. Suponha que desejemos ter certeza que sempre criaremos instâncias de subclasses da classe SchoolMember e que não criaremos instâncias da classe SchoolMember em si.

Podemos garantir isso usando um conceito chamado de classe base abstrata (abstract base class). Isso significa que a classe será abstrata o que quer dizer que a classe em si é um conceito, e não deve ser utilizada como uma classe real (ou seja, não devem ser criados objetos - instâncias - dessa classe).

Podemos declarar que a nossa classe é uma classe base abstrata usando a metaclasse interna chamada ABCMeta (ABC - abstract base class).

#!/usr/bin/env python
# Arquivo: inherit_abc.py
 
from abc import *
 
class SchoolMember(metaclass=ABCMeta):
    '''Representa um membro qualquer da escola.'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(Iniciado SchoolMember: {0})'.format(self.name))
 
    @abstractmethod
    def tell(self):
        '''Imprime os dados da instância.'''
        print('Nome:"{0}" Idade:"{1}"'.format(self.name, self.age), end=" ")
 
class Teacher(SchoolMember):
    '''Representa um professor.'''
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(Iniciado Teacher: {0})'.format(self.name))
 
    def tell(self):
        SchoolMember.tell(self)
        print('Salário: "{0:d}"'.format(self.salary))
 
class Student(SchoolMember):
    '''Representa um aluno.'''
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Iniciado Student: {0})'.format(self.name))
 
    def tell(self):
        SchoolMember.tell(self)
        print('Nota: "{0:d}"'.format(self.marks))
 
t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)
 
m = SchoolMember('abc', 10)
 
m.tell()
 
print() # imprime uma linha em branco
 
members = [t, s]
for member in members:
    member.tell() # funciona tanto para Teacher como para Student

Saída:

   (Iniciado SchoolMember: Mrs. Shrividya)
   (Iniciado Teacher: Mrs. Shrividya)
   (Iniciado SchoolMember: Swaroop)
   (Iniciado Student: Swaroop)
   Traceback (most recent call last):
     File "C:\Users\swaroop\python\usingabc.py", line 44, in <module>
       m = SchoolMember('abc', 10)
   TypeError: Can't instantiate abstract class SchoolMember with abstract methods tell

Como funciona:

Podemos declarar o método tell da classe SchoolMember como sendo um método abstrato, assim, automaticamente, não poderemos criar instâncias da classe SchoolMember.

Entretanto, podermos criar instâncias de Teacher e Student como se fossem instâncias de SchoolMember, por que eles são subclasses.

[edit] Sumário

Acabamos de explorar os vários aspectos de classes e objetos bem como várias terminologias relacionadas. Vimos também os benefícios e as armadilhas da programação orientada a objetos. Python é fortemente orientado a objetos e compreender cuidadosamente estes conceitos o ajudarão muito a longo prazo.

A seguir, aprenderemos a lidar com entrada e saída (input/output) e sobre como acessar arquivos em Python.



Please add your comments by clicking on the 'Discussion' link in the left sidebar.