Objetos em Python
Classes em Python
O código a seguir ilustra uma classe muito simples representada em Python:
class Pessoa:
pass
A sintaxe para a definição de uma classe é: class <classe>:
onde:
class
é a palavra do Python que indica a definição de uma classe<classe>
é o nome da classe:
representa o terminador
Simples assim: o código cria a definição da classe Pessoa
.
O conteúdo da classe é representado na sequência, como um código Python convencional, usando "tab" para indentação. Como essa classe não faz coisa alguma usamos apenas a palavra pass
.
Instanciação
Para criar um objeto a partir da classe usamos o processo de instanciação, que significa a criação de um objeto a partir de uma classe. O trecho de código a seguir ilustra esse processo, considerando a classe Pessoa
.
jose = Pessoa()
maria = Pessoa()
A sintaxe para instanciação é: <identificador> = <classe>()
onde:
<identificador>
é o nome do objeto, como o nome de uma variável (ex.:jose
)=
<classe>
é o nome da classe (ex.:Pessoa
)()
Parece com a chamada de uma função, mas o nome é de uma classe, por isso o Python entende o que deve fazer. O exemplo mostra como são criados dois objetos, jose
e maria
, ambos instâncias da classe Pessoa
.
Como o Python representa objetos? Quando um código imprime um valor str
vemos os caracteres. Quando imprime um objeto vemos um endereço de memória. Veja:
>>> print(jose)
<__main__.Pessoa object at 0x00000246389AB048>
>>> print(maria)
<__main__.Pessoa object at 0x00000246389AB198>
A saída da execução desse código (no terminal interativo do Python) demonstra que os endereços de memória são representados como números hexadecimais.
Trabalhando com atributos
A classe Pessoa
não representa muita coisa até agora, ela não contém dados ou comportamentos. Como adicionamos dados ao objeto? Em Python não é necessário definir um atributo na classe para que ele esteja disponível no objeto. Na verdade, podemos definir atributos diretamente em um objeto (sem definir na classe) usando a notação ponto, como demonstra o código a seguir:
class Pessoa():
pass
jose = Pessoa()
maria = Pessoa()
jose.nome = 'José'
maria.nome = 'Maria'
maria.idade = 20
print('jose.nome =', jose.nome)
print('maria.nome =', maria.nome)
print('maria.idade =', maria.idade)
A saída da execução do código seria:
jose.nome = José
maria.nome = Maria
maria.idade = 20
A sintaxe para criar um atributo em um objeto e definir seu valor usando a notação ponto é: <objeto>.<atributo> = <valor>
onde:
<objeto>
é o identificador, nome do objeto.
<atributo>
é o identificador, nome do atributo=
<valor>
é o valor armazenado no atributo
Entretanto, essa não é uma boa prática de projeto orientado a objeto. Como já vimos, a classe é quem determina as características de um objeto, então os atributos devem ser declarados na classe, não no objeto, caso contrário podemos ter objetos da mesma classe, mas com estruturas diferentes. Vamos ver como resolver isso daqui a pouco ao tratar sobre o método construtor.
Trabalhando com métodos
Já sabemos que enquanto os atributos definem dados os métodos definem comportamentos. Para representar métodos em Python usamos uma sintaxe semelhante à de uma função, com algumas diferenças:
- o método deve ser declarado no contexto da classe
- o método deve ter, obrigatoriamente, o parâmetro
self
Para ilustrar isso, vamos declarar o método falar
na classe Pessoa
:
class Pessoa:
def falar(self):
pass
A sintaxe é: def <método>(<parâmetros>):
onde:
def
é a palavra Python para declarar o método<método>
é o nome do método(
<parâmetros>
é uma lista de parâmetros (mais sobre isso daqui a pouco))
:
O conteúdo do método vem logo na sequência, como uma função. No caso do exemplo, o método é inútil (seu conteúdo é apenas pass
).
Uma das coisas mais legais de trabalhar com métodos é que isso permite entender melhor o conceito de mensagens, visto anteriormente. Veja o código a seguir:
jose = Pessoa()
maria = Pessoa()
jose.falar()
maria.falar()
O código declara duas instâncias de Pessoa
. Por fim, usando notação ponto, indica que deve passada uma mensagem para cada objeto. Para isso, usa a sintaxe: <objeto>.<método>()
onde:
<objeto>
é o identificador do objeto.
<método>
é o identificador do método()
Pronto. Passar uma mensagem para um objeto significa chamar um método dele. O código ilustra a passagem de mensagem para dois objetos, jose
e maria
. Perceba que sempre é necessário indicar para qual objeto deve ser passada a mensagem. Em outras palavras, sempre é necessário indicar o objeto e o método a ser chamado.
A UML fornece o Diagrama de Sequência, uma forma gráfica de demonstrar a interação entre usuário do sistema e objetos ou camadas. A figura a seguir demonstra a utilização do Diagrama de Sequência nesse contexto.
O Diagrama de Sequência demonstra os passos:
- o
Usuário
interage comMain
(o programa principal) Main
interage com o objetojose
, do tipoPessoa
, enviando a mensagemfalar('oi')
- o objeto
jose
trata, executa a mensagem e retorna paraMain
Main
apresenta a mensagem para oUsuário
Esse procedimento se repete para o objeto maria
.
Métodos com parâmetros
O método falar
, como já vimos, é bem inútil. Para adicionar algo mais significativo considere que o objetivo do método é implementar o requisito:
Uma pessoa deve falar uma mensagem.
Para implementar isso vamos utilizar parâmetros. Veja o código:
class Pessoa:
def falar(self, mensagem):
print(mensagem)
A sintaxe para definição do método não muda, mas agora ele possui dois parâmetros:
self
(obrigatório por causa da sintaxe de definição de método em Python)mensagem
: a mensagem que será impressa
De fato, porque self
é obrigatório, daqui para frente não vamos ficar falando isso, ou seja, é implícito.
A definição do método falar faz com que a passagem de mensagem, a chamada do método seja diferente. Veja:
jose = Pessoa()
maria = Pessoa()
jose.falar('Oi, eu sou o José')
maria.falar('Oieee! Eu sou a Maria :*')
A saída da execução mostra a mensagem falada por cada pessoa.
Método construtor
Para declarar atributos na classe podemos utilizar um construtor, um método especial que inicializa o objeto e define a sua estrutura (seus atributos) durante o processo de instanciação. No Python o construtor é chamado __init__
e podemos utilizá-lo como ilustra o código a seguir.
class Pessoa:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
Os parâmetros do construtor são:
nome
: representa o nome da pessoaidade
: representa a idade da pessoa
Perceba que o código do construtor utiliza uma estrutura importante: self.nome = nome
onde self
é a palavra Python que representa uma referência para o objeto atual e a instrução significa, especificamente, armazenar o valor do parâmetro nome
no atributo nome
do objeto. Por "objeto atual" quero dizer aquele que está no contexto da execução do código. Veja o código a seguir:
jose = Pessoa('jose', 1)
Neste momento é chamado o construtor e self
é uma referência para o objeto jose
, ou seja, outro identificador utilizado para acessar o mesmo objeto. Lembra do endereço de memória? Olha só:
jose = Pessoa()
maria = jose
print(jose)
print(maria)
A saída da execução do código seria algo como:
>>> print(jose)
<__main__.Pessoa object at 0x000002B0F101B080>
>>> print(maria)
<__main__.Pessoa object at 0x000002B0F101B080>
Percebeu que os endereços de memória são os mesmos? Por isso podemos entender que a atribuição maria = jose
não pode ser entendida como armazena o valor de jose em maria, mas armazena em maria
o mesmo endereço de memória de jose
.
O que acontece com a saída do código a seguir?
jose = Pessoa()
jose.nome = 'José'
maria = jose
maria.nome = 'Maria'
print(jose.nome)
print(maria.nome)
Referências são utilizadas em linguagens de programação orientadas a objetos para muito mais coisas que construtores. Vamos continuar vendo sobre esse assunto mais adiante.
A utilização do construtor dessa forma modifica a instanciação. Veja:
jose = Pessoa('José', 21)
maria = Pessoa('Maria', 19)
Assim, temos uma nova leitura para sintaxe da instanciação: <identificador> = <classe>(<argumentos>)
onde:
<identificador>
é o nome do objeto, como o nome de uma variável (ex.:jose
)=
<classe>
é o nome da classe (ex.:Pessoa
)(
<argumentos>
: representa a lista de argumentos, conforme a definição do construtor)