Programação Orientada a Objetos em Python: Guia Completo
O Python tem sido uma linguagem orientada a objetos desde que existe. Por causa disso, criar e usar classes e objetos é extremamente fácil. Este capítulo ajuda você a se tornar um especialista no uso do suporte de programação orientada a objetos do Python.
Se você não tem experiência prévia com programação orientada a objetos (OO), pode querer consultar um curso introdutório sobre isso ou pelo menos um tutorial de algum tipo para que você tenha uma noção dos conceitos básicos.
No entanto, aqui está uma pequena introdução à Programação Orientada a Objetos (POO) para colocá-lo em dia:
Visão Geral da Terminologia POO
Classe - Um protótipo definido pelo usuário para um objeto que define um conjunto de atributos que caracterizam qualquer objeto da classe. Os atributos são membros de dados (variáveis de classe e variáveis de instância) e métodos, acessados via notação de ponto.
Variável de classe - Uma variável compartilhada por todas as instâncias de uma classe. Variáveis de classe são definidas dentro de uma classe, mas fora de qualquer método da classe. Variáveis de classe não são usadas com tanta frequência quanto variáveis de instância.
Membro de dados - Uma variável de classe ou variável de instância que contém dados associados a uma classe e seus objetos.
Sobrecarga de função - A atribuição de mais de um comportamento a uma função particular. A operação realizada varia pelos tipos de objetos ou argumentos envolvidos.
Variável de instância - Uma variável definida dentro de um método e que pertence apenas à instância atual de uma classe.
Herança - A transferência das características de uma classe para outras classes derivadas dela.
Instância - Um objeto individual de uma certa classe. Um objeto obj que pertence a uma classe Circle, por exemplo, é uma instância da classe Circle.
Instanciação - A criação de uma instância de uma classe.
Método - Um tipo especial de função definida em uma definição de classe.
Objeto - Uma instância única de uma estrutura de dados definida por sua classe. Um objeto compreende tanto membros de dados (variáveis de classe e variáveis de instância) quanto métodos.
Sobrecarga de operador - A atribuição de mais de uma função a um operador particular.
Criando Classes
A instrução class cria uma nova definição de classe. O nome da classe segue imediatamente a palavra-chave class seguida por dois pontos:
class ClassName:
'Optional class documentation string'
class_suite
A classe tem uma string de documentação, que pode ser acessada via
ClassName.__doc__.O
class_suiteconsiste em todas as instruções de componente que definem membros da classe, atributos de dados e funções.
Exemplo
Segue o exemplo de uma classe Python simples:
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
A variável
empCounté uma variável de classe cujo valor é compartilhado entre todas as instâncias desta classe. Isso pode ser acessado comoEmployee.empCountde dentro da classe ou fora da classe.O primeiro método
__init__()é um método especial, chamado construtor da classe ou método de inicialização que o Python chama quando você cria uma nova instância desta classe.Você declara outros métodos de classe como funções normais, com a exceção de que o primeiro argumento de cada método é
self. O Python adiciona o argumentoselfà lista para você; você não precisa incluí-lo quando chama os métodos.
Criando Objetos de Instância
Para criar instâncias de uma classe, você chama a classe usando o nome da classe e passa quaisquer argumentos que seu método __init__ aceite.
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
Acessando Atributos
Você acessa os atributos do objeto usando o operador de ponto com o objeto. Variável de classe seria acessada usando o nome da classe da seguinte forma:
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
Agora, juntando todos os conceitos:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
Quando o código acima é executado, produz o seguinte resultado:
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
Você pode adicionar, remover ou modificar atributos de classes e objetos a qualquer momento:
emp1.age = 7 # Adiciona um atributo 'age'.
emp1.age = 8 # Modifica atributo 'age'.
del emp1.age # Deleta atributo 'age'.
Em vez de usar as instruções normais para acessar atributos, você pode usar as seguintes funções:
getattr(obj, name[, default]) - para acessar o atributo do objeto.
hasattr(obj, name) - para verificar se um atributo existe ou não.
setattr(obj, name, value) - para definir um atributo. Se o atributo não existir, ele será criado.
delattr(obj, name) - para deletar um atributo.
hasattr(emp1, 'age') # Retorna true se atributo 'age' existe
getattr(emp1, 'age') # Retorna valor do atributo 'age'
setattr(emp1, 'age', 8) # Define atributo 'age' como 8
delattr(emp1, 'age') # Deleta atributo 'age'
Atributos de Classe Embutidos
Toda classe Python mantém os seguintes atributos embutidos e eles podem ser acessados usando o operador de ponto como qualquer outro atributo:
__dict__ - Dicionário contendo o namespace da classe.
__doc__ - String de documentação da classe ou none, se indefinida.
__name__ - Nome da classe.
__module__ - Nome do módulo no qual a classe é definida. Este atributo é "__main__" no modo interativo.
__bases__ - Uma tupla possivelmente vazia contendo as classes base, na ordem de sua ocorrência na lista de classes base.
Para a classe acima, vamos tentar acessar todos esses atributos:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
Quando o código acima é executado, produz o seguinte resultado:
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': , 'empCount': 2, 'displayEmployee': , '__doc__': 'Common base class for all employees', '__init__': }
Destruindo Objetos (Coleta de Lixo)
O Python deleta objetos desnecessários (tipos embutidos ou instâncias de classe) automaticamente para liberar espaço de memória. O processo pelo qual o Python periodicamente recupera blocos de memória que não estão mais em uso é denominado Coleta de Lixo.
O coletor de lixo do Python é executado durante a execução do programa e é acionado quando a contagem de referências de um objeto chega a zero. A contagem de referências de um objeto muda conforme o número de aliases que apontam para ele muda.
A contagem de referências de um objeto aumenta quando ele é atribuído a um novo nome ou colocado em um container (lista, tupla ou dicionário). A contagem de referências do objeto diminui quando é deletado com del, sua referência é reatribuída ou sua referência sai do escopo. Quando a contagem de referências de um objeto chega a zero, o Python o coleta automaticamente.
a = 40 # Cria objeto <40>
b = a # Aumenta cont. de ref. de <40>
c = [b] # Aumenta cont. de ref. de <40>
del a # Diminui cont. de ref. de <40>
b = 100 # Diminui cont. de ref. de <40>
c[0] = -1 # Diminui cont. de ref. de <40>
Normalmente, você não notará quando o coletor de lixo destrói uma instância órfã e recupera seu espaço. Mas uma classe pode implementar o método especial __del__(), chamado destrutor, que é invocado quando a instância está prestes a ser destruída. Este método pode ser usado para limpar quaisquer recursos não de memória usados por uma instância.
Exemplo
Este destrutor __del__() imprime o nome da classe de uma instância que está prestes a ser destruída:
#!/usr/bin/python
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print(class_name, "destroyed")
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3)) # imprime os ids dos objetos
del pt1
del pt2
del pt3
Quando o código acima é executado, produz o seguinte resultado:
3083401324 3083401324 3083401324
Point destroyed
Nota - Idealmente, você deve definir suas classes em arquivo separado, então deve importá-las no seu arquivo de programa principal usando a instrução import.
Herança de Classe
Em vez de começar do zero, você pode criar uma classe derivando-a de uma classe preexistente listando a classe pai entre parênteses após o nome da nova classe.
A classe filha herda os atributos de sua classe pai, e você pode usar esses atributos como se fossem definidos na classe filha. Uma classe filha também pode sobrescrever membros de dados e métodos do pai.
Sintaxe
Classes derivadas são declaradas muito parecidas com sua classe pai; no entanto, uma lista de classes base para herdar é dada após o nome da classe:
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
Exemplo
#!/usr/bin/python
class Parent:
# define parent class
parentAttr = 100
def __init__(self):
print("Calling parent constructor")
def parentMethod(self):
print('Calling parent method')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("Parent attribute :", Parent.parentAttr)
class Child(Parent):
# define child class
def __init__(self):
print("Calling child constructor")
def childMethod(self):
print('Calling child method')
c = Child() # instância da child
c.childMethod() # child chama seu método
c.parentMethod() # chama método do parent
c.setAttr(200) # novamente chama método do parent
c.getAttr() # novamente chama método do parent
Quando o código acima é executado, produz o seguinte resultado:
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
De forma similar, você pode derivar uma classe de múltiplas classes pai da seguinte forma:
class A:
# define sua classe A .....
class B:
# define sua classe B .....
class C(A, B):
# subclasse de A e B .....
Você pode usar as funções issubclass() ou isinstance() para verificar relações entre duas classes e instâncias.
A função booleana issubclass(sub, sup) retorna true se a subclasse dada sub for de fato uma subclasse da superclasse sup.
A função booleana isinstance(obj, Class) retorna true se obj for uma instância da classe Class ou for uma instância de uma subclasse de Class.
Sobrescrevendo Métodos
Você sempre pode sobrescrever os métodos da sua classe pai. Uma razão para sobrescrever métodos do pai é porque você pode querer funcionalidade especial ou diferente na sua subclasse.
Exemplo
#!/usr/bin/python
class Parent:
# define parent class
def myMethod(self):
print('Calling parent method')
class Child(Parent):
# define child class
def myMethod(self):
print('Calling child method')
c = Child() # instância da child
c.myMethod() # child chama método sobrescrito
Quando o código acima é executado, produz o seguinte resultado:
Calling child method
Métodos de Sobrecarga Base
A tabela a seguir lista algumas funcionalidades genéricas que você pode sobrescrever em suas próprias classes:
| Nº | Método, Descrição e Chamada de Exemplo |
|---|---|
| 1 | __init__ ( self [,args...] ) Construtor (com quaisquer argumentos opcionais) Chamada de Exemplo: obj = className(args) |
| 2 | __del__( self ) Destrutor, deleta um objeto Chamada de Exemplo: del obj |
| 3 | __repr__( self ) Representação em string avaliável Chamada de Exemplo: repr(obj) |
| 4 | __str__( self ) Representação em string imprimível Chamada de Exemplo: str(obj) |
| 5 | __cmp__ ( self, x ) Comparação de objetos Chamada de Exemplo: cmp(obj, x) |
Sobrecarga de Operadores
Suponha que você criou uma classe Vector para representar vetores bidimensionais. O que acontece quando você usa o operador mais para somá-los? Muito provavelmente o Python vai reclamar.
Você poderia, no entanto, definir o método __add__ em sua classe para realizar adição de vetores e então o operador mais se comportaria como esperado:
Exemplo
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
Quando o código acima é executado, produz o seguinte resultado:
Vector(7, 8)
Ocultação de Dados
Os atributos de um objeto podem ou não ser visíveis fora da definição da classe. Você precisa nomear atributos com um prefixo de sublinhado duplo, e esses atributos então não serão diretamente visíveis para outsiders.
Exemplo
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print(counter.__secretCount)
Quando o código acima é executado, produz o seguinte resultado:
1
2
Traceback (most recent call last):
File "test.py", line 12, in
print(counter.__secretCount)
AttributeError: JustCounter instance has no attribute '__secretCount'
O Python protege esses membros alterando internamente o nome para incluir o nome da classe. Você pode acessar tais atributos como object._className__attrName. Se você substituir sua última linha pela seguinte, funcionará para você:
print(counter._JustCounter__secretCount)
Quando o código acima é executado, produz o seguinte resultado:
1
2
2
python
- Classes e Objetos em C#: Fundamentos para POO Robusta
- Dominando Tipos de Dados em Python: Guia Prático
- Dominando Operadores em Python: Guia Completo
- Dominando a Declaração Pass em Python: Guia Prático
- Dominando Dicionários em Python: Criação, Manipulação e Técnicas Avançadas
- Dominando Exceções Customizadas em Python: Guia Prático
- Dominando Programação Orientada a Objetos em Python: Classes, Herança, Encapsulamento e Polimorfismo
- Dominando Herança em Python: Conceitos, Sintaxe e Exemplos Práticos
- Padrão Singleton em Java Explicado: Como Implementar e Usar com Segurança
- Rastreamento Automatizado de Objetos Baseado em Visão com Raspberry Pi e OpenCV