Introdução
As funcionalidades de programação orientada a objetos (POO) do Python oferecem ferramentas poderosas para criar objetos expressivos e personalizados. Dentre estas, os métodos especiais __init__, __str__ e __repr__ desempenham papéis cruciais na definição do comportamento e da representação de seus objetos Python.
Neste tutorial, exploraremos esses métodos especiais, entenderemos seus propósitos e aprenderemos como implementá-los efetivamente em suas classes Python. Ao final deste laboratório, você será capaz de criar objetos Python mais intuitivos e fáceis de usar.
Criando Classes com o Método __init__
O primeiro método especial que exploraremos é o __init__, que é chamado quando um objeto é criado. Este método permite que você inicialize os atributos do seu objeto.
Entendendo o Método __init__
O método __init__, também conhecido como construtor (constructor), é chamado automaticamente quando você cria uma nova instância de uma classe. Seu objetivo principal é configurar o estado inicial do objeto, atribuindo valores aos atributos.
Vamos começar criando uma classe Python simples com o método __init__:
Abra o terminal no seu ambiente LabEx.
Navegue até o diretório do projeto:
cd ~/projectCrie um novo arquivo Python chamado
person.pyusando o editor de código:No editor, adicione o seguinte código a
person.py:class Person: def __init__(self, name, age): self.name = name self.age = age ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes print(f"Name: {person1.name}") print(f"Age: {person1.age}")Salve o arquivo.
Execute o script Python:
python3 person.py
Você deve ver a seguinte saída:
Name: Alice
Age: 30
Explicação do Método __init__
No código acima:
- Definimos uma classe
Personcom um método__init__que recebe três parâmetros:self,nameeage. - O parâmetro
selfse refere à instância que está sendo criada e é passado automaticamente quando um objeto é criado. - Dentro do método
__init__, atribuímos os valores denameeageaos atributos do objeto usandoself.nameeself.age. - Quando criamos um novo objeto
Personcomperson1 = Person("Alice", 30), o método__init__é chamado automaticamente. - Podemos então acessar os atributos usando a notação de ponto:
person1.nameeperson1.age.
Adicionando Mais Funcionalidade
Vamos aprimorar nossa classe Person adicionando um método para calcular o ano de nascimento com base no ano atual e na idade da pessoa:
Abra
person.pyno editor.Atualize o código para incluir um método para calcular o ano de nascimento:
import datetime class Person: def __init__(self, name, age): self.name = name self.age = age def birth_year(self): current_year = datetime.datetime.now().year return current_year - self.age ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes and call the method print(f"Name: {person1.name}") print(f"Age: {person1.age}") print(f"Birth Year: {person1.birth_year()}")Salve o arquivo.
Execute o script Python:
python3 person.py
A saída agora deve incluir o ano de nascimento calculado:
Name: Alice
Age: 30
Birth Year: 1993
(O ano de nascimento real dependerá do ano atual quando você executar o script)
Agora você criou uma classe Python com o método __init__ para inicializar os atributos do objeto e adicionou um método para realizar cálculos com base nesses atributos.
Implementando o Método __str__
Agora que entendemos como criar uma classe com o método __init__, vamos explorar outro método especial chamado __str__. Este método nos permite definir como um objeto deve ser representado como uma string.
Entendendo o Método __str__
O método __str__ é chamado quando você usa a função str() em um objeto ou quando você imprime um objeto usando a função print(). Ele deve retornar uma representação de string legível por humanos do objeto.
Vamos atualizar nossa classe Person para incluir um método __str__:
Abra
person.pyno editor.Atualize o código para incluir um método
__str__:import datetime class Person: def __init__(self, name, age): self.name = name self.age = age def birth_year(self): current_year = datetime.datetime.now().year return current_year - self.age def __str__(self): return f"{self.name}, {self.age} years old" ## Create a Person object person1 = Person("Alice", 30) ## Access the attributes and call the method print(f"Name: {person1.name}") print(f"Age: {person1.age}") print(f"Birth Year: {person1.birth_year()}") ## Use the __str__ method implicitly print("\nString representation of the object:") print(person1) ## Use the __str__ method explicitly print("\nExplicit string conversion:") print(str(person1))Salve o arquivo.
Execute o script Python:
python3 person.py
Você deve ver uma saída semelhante a esta:
Name: Alice
Age: 30
Birth Year: 1993
String representation of the object:
Alice, 30 years old
Explicit string conversion:
Alice, 30 years old
Como __str__ Funciona
No código acima:
- Definimos um método
__str__que retorna uma string formatada com o nome e a idade da pessoa. - Quando chamamos
print(person1), o Python chama automaticamente o método__str__para determinar o que exibir. - Também podemos converter explicitamente um objeto em uma string usando
str(person1), que também chama o método__str__.
O Que Acontece Sem __str__
Para entender a importância do método __str__, vamos ver o que acontece quando não o definimos:
Crie um novo arquivo chamado
without_str.py:Adicione o seguinte código:
class SimpleClass: def __init__(self, value): self.value = value ## Create an object obj = SimpleClass(42) ## Print the object print(obj)Salve o arquivo.
Execute o script:
python3 without_str.py
Você deve ver uma saída como:
<__main__.SimpleClass object at 0x7f2d8c3e9d90>
A saída não é muito informativa. Ela mostra o nome da classe e o endereço de memória do objeto, mas não seu conteúdo. É por isso que implementar um método __str__ adequado é importante para tornar seus objetos mais fáceis de usar.
Exercício Prático
Vamos criar uma nova classe chamada Book com um método __str__:
Crie um novo arquivo chamado
book.py:Adicione o seguinte código:
class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def __str__(self): return f'"{self.title}" by {self.author} ({self.pages} pages)' ## Create book objects book1 = Book("The Great Gatsby", "F. Scott Fitzgerald", 180) book2 = Book("To Kill a Mockingbird", "Harper Lee", 281) ## Print the books print(book1) print(book2)Salve o arquivo.
Execute o script:
python3 book.py
A saída deve ser:
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
Agora você entende como usar o método __str__ para criar representações de string legíveis por humanos de seus objetos.
Implementando o Método __repr__
Além de __str__, o Python fornece outro método especial para representação de string: __repr__. Enquanto __str__ tem como objetivo fornecer uma representação legível por humanos, __repr__ se destina a fornecer uma representação inequívoca de um objeto que pode ser usada para recriar o objeto, se possível.
Entendendo o Método __repr__
O método __repr__ é chamado quando você usa a função repr() em um objeto ou quando você exibe um objeto em uma sessão interativa. Ele deve retornar uma string que, quando passada para eval(), criaria um objeto equivalente (quando possível).
Vamos atualizar nossa classe Book para incluir um método __repr__:
Abra
book.pyno editor.Atualize o código para incluir um método
__repr__:class Book: def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def __str__(self): return f'"{self.title}" by {self.author} ({self.pages} pages)' def __repr__(self): return f'Book("{self.title}", "{self.author}", {self.pages})' ## Create book objects book1 = Book("The Great Gatsby", "F. Scott Fitzgerald", 180) book2 = Book("To Kill a Mockingbird", "Harper Lee", 281) ## Print the books (uses __str__) print("String representation (using __str__):") print(book1) print(book2) ## Get the representation (uses __repr__) print("\nRepresentation (using __repr__):") print(repr(book1)) print(repr(book2))Salve o arquivo.
Execute o script:
python3 book.py
Você deve ver uma saída como:
String representation (using __str__):
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
Representation (using __repr__):
Book("The Great Gatsby", "F. Scott Fitzgerald", 180)
Book("To Kill a Mockingbird", "Harper Lee", 281)
Diferenças Entre __str__ e __repr__
As principais diferenças entre __str__ e __repr__ são:
__str__é destinado à saída legível por humanos, enquanto__repr__é destinado a desenvolvedores e depuração.- Se
__str__não for definido, mas__repr__for, o Python usará__repr__como um fallback parastr()ouprint(). __repr__deve, idealmente, retornar uma string que possa recriar o objeto quando passada paraeval(), embora isso nem sempre seja possível ou necessário.
A Função eval() com __repr__
Quando implementado corretamente, a string retornada por __repr__ pode ser usada com eval() para recriar o objeto. Vamos ver isso em ação:
Crie um novo arquivo chamado
repr_eval.py:Adicione o seguinte código:
class Point: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Point at ({self.x}, {self.y})" def __repr__(self): return f"Point({self.x}, {self.y})" ## Create a point p1 = Point(3, 4) ## Get the repr string repr_str = repr(p1) print(f"Representation: {repr_str}") ## Use eval to recreate the object p2 = eval(repr_str) print(f"Recreated object: {p2}") ## Verify they have the same values print(f"p1.x = {p1.x}, p1.y = {p1.y}") print(f"p2.x = {p2.x}, p2.y = {p2.y}")Salve o arquivo.
Execute o script:
python3 repr_eval.py
Você deve ver uma saída como:
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
Isso demonstra que podemos recriar o objeto original usando a string retornada por __repr__ e a função eval().
Quando Usar Cada Método
- Use
__init__para configurar o estado inicial de seus objetos. - Use
__str__para fornecer uma representação legível por humanos para usuários finais. - Use
__repr__para fornecer uma representação precisa e inequívoca para desenvolvedores e depuração.
Exercício Prático: Um Exemplo Completo
Vamos juntar tudo criando uma classe Rectangle com todos os três métodos especiais:
Crie um novo arquivo chamado
rectangle.py:Adicione o seguinte código:
class Rectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) def __str__(self): return f"Rectangle with width {self.width} and height {self.height}" def __repr__(self): return f"Rectangle({self.width}, {self.height})" ## Create rectangles rect1 = Rectangle(5, 10) rect2 = Rectangle(3, 7) ## Display information about the rectangles print(f"Rectangle 1: {rect1}") print(f"Area: {rect1.area()}") print(f"Perimeter: {rect1.perimeter()}") print(f"Representation: {repr(rect1)}") print("\nRectangle 2: {0}".format(rect2)) print(f"Area: {rect2.area()}") print(f"Perimeter: {rect2.perimeter()}") print(f"Representation: {repr(rect2)}") ## Recreate a rectangle using eval rect3 = eval(repr(rect1)) print(f"\nRecreated rectangle: {rect3}") print(f"Is it the same area? {rect3.area() == rect1.area()}")Salve o arquivo.
Execute o script:
python3 rectangle.py
Você deve ver uma saída como:
Rectangle 1: Rectangle with width 5 and height 10
Area: 50
Perimeter: 30
Representation: Rectangle(5, 10)
Rectangle 2: Rectangle with width 3 and height 7
Area: 21
Perimeter: 20
Representation: Rectangle(3, 7)
Recreated rectangle: Rectangle with width 5 and height 10
Is it the same area? True
Este exemplo demonstra como todos os três métodos especiais (__init__, __str__ e __repr__) trabalham juntos para criar uma classe bem projetada.
Criando uma Aplicação Prática
Agora que entendemos os três métodos especiais __init__, __str__ e __repr__, vamos criar uma aplicação mais prática que demonstra seu uso em um cenário do mundo real. Criaremos um sistema bancário simples com uma classe BankAccount.
Construindo uma Classe Bank Account
Crie um novo arquivo chamado
bank_account.py:Adicione o seguinte código:
class BankAccount: def __init__(self, account_number, owner_name, balance=0.0): self.account_number = account_number self.owner_name = owner_name self.balance = balance self.transactions = [] def deposit(self, amount): if amount <= 0: print("Deposit amount must be positive") return False self.balance += amount self.transactions.append(f"Deposit: +${amount:.2f}") return True def withdraw(self, amount): if amount <= 0: print("Withdrawal amount must be positive") return False if amount > self.balance: print("Insufficient funds") return False self.balance -= amount self.transactions.append(f"Withdrawal: -${amount:.2f}") return True def get_transaction_history(self): return self.transactions def __str__(self): return f"Account {self.account_number} | Owner: {self.owner_name} | Balance: ${self.balance:.2f}" def __repr__(self): return f'BankAccount("{self.account_number}", "{self.owner_name}", {self.balance})'Salve o arquivo.
Testando a Classe Bank Account
Agora vamos testar nossa classe BankAccount:
Crie um novo arquivo chamado
bank_test.py:Adicione o seguinte código:
from bank_account import BankAccount ## Create bank accounts account1 = BankAccount("12345", "John Doe", 1000.0) account2 = BankAccount("67890", "Jane Smith", 500.0) ## Display initial account information print("Initial Account Status:") print(account1) print(account2) ## Perform transactions print("\nPerforming transactions...") ## Deposit to account1 print("\nDepositing $250 to account1:") account1.deposit(250) print(account1) ## Withdraw from account2 print("\nWithdrawing $100 from account2:") account2.withdraw(100) print(account2) ## Try to withdraw too much from account2 print("\nTrying to withdraw $1000 from account2:") account2.withdraw(1000) print(account2) ## Try to deposit a negative amount to account1 print("\nTrying to deposit -$50 to account1:") account1.deposit(-50) print(account1) ## Display transaction history print("\nTransaction history for account1:") for transaction in account1.get_transaction_history(): print(f"- {transaction}") print("\nTransaction history for account2:") for transaction in account2.get_transaction_history(): print(f"- {transaction}") ## Recreate an account using repr print("\nRecreating account1 using repr:") account1_repr = repr(account1) print(f"Representation: {account1_repr}") recreated_account = eval(account1_repr) print(f"Recreated account: {recreated_account}")Salve o arquivo.
Execute o script:
python3 bank_test.py
Você deve ver uma saída semelhante a esta:
Initial Account Status:
Account 12345 | Owner: John Doe | Balance: $1000.00
Account 67890 | Owner: Jane Smith | Balance: $500.00
Performing transactions...
Depositing $250 to account1:
Account 12345 | Owner: John Doe | Balance: $1250.00
Withdrawing $100 from account2:
Account 67890 | Owner: Jane Smith | Balance: $400.00
Trying to withdraw $1000 from account2:
Insufficient funds
Account 67890 | Owner: Jane Smith | Balance: $400.00
Trying to deposit -$50 to account1:
Deposit amount must be positive
Account 12345 | Owner: John Doe | Balance: $1250.00
Transaction history for account1:
- Deposit: +$250.00
Transaction history for account2:
- Withdrawal: -$100.00
Recreating account1 using repr:
Representation: BankAccount("12345", "John Doe", 1250.0)
Recreated account: Account 12345 | Owner: John Doe | Balance: $1250.00
Entendendo a Implementação
Nesta aplicação bancária:
O método
__init__inicializa uma conta bancária com um número de conta, nome do proprietário e um saldo inicial opcional. Ele também cria uma lista vazia para rastrear as transações.Os métodos
depositewithdrawlidam com as transações e atualizam o saldo e o histórico de transações.O método
__str__fornece uma representação amigável da conta, mostrando o número da conta, o nome do proprietário e o saldo atual.O método
__repr__fornece uma string que pode recriar o objeto da conta (embora observe que o histórico de transações não é preservado nesta implementação).
Este exemplo demonstra como esses métodos especiais podem ser usados em uma aplicação prática para criar objetos mais intuitivos e fáceis de usar.
Estendendo a Aplicação
Como um exercício final, vamos criar um sistema bancário simples que gerencia várias contas:
Crie um novo arquivo chamado
banking_system.py:Adicione o seguinte código:
from bank_account import BankAccount class BankingSystem: def __init__(self, bank_name): self.bank_name = bank_name self.accounts = {} def create_account(self, account_number, owner_name, initial_balance=0.0): if account_number in self.accounts: print(f"Account {account_number} already exists") return None account = BankAccount(account_number, owner_name, initial_balance) self.accounts[account_number] = account return account def get_account(self, account_number): return self.accounts.get(account_number) def list_accounts(self): return list(self.accounts.values()) def __str__(self): return f"{self.bank_name} - Managing {len(self.accounts)} accounts" def __repr__(self): return f'BankingSystem("{self.bank_name}")' ## Create a banking system bank = BankingSystem("Python First Bank") print(bank) ## Create some accounts bank.create_account("A001", "John Doe", 1000) bank.create_account("A002", "Jane Smith", 500) bank.create_account("A003", "Bob Johnson", 250) ## List all accounts print("\nAll accounts:") for account in bank.list_accounts(): print(account) ## Make some transactions account = bank.get_account("A001") if account: print(f"\nBefore deposit: {account}") account.deposit(500) print(f"After deposit: {account}") account = bank.get_account("A002") if account: print(f"\nBefore withdrawal: {account}") account.withdraw(200) print(f"After withdrawal: {account}") ## Try to create an existing account print("\nTrying to create an existing account:") bank.create_account("A001", "Someone Else", 300) ## Final state of the banking system print(f"\n{bank}")Salve o arquivo.
Execute o script:
python3 banking_system.py
Você deve ver uma saída semelhante a:
Python First Bank - Managing 0 accounts
All accounts:
Account A001 | Owner: John Doe | Balance: $1000.00
Account A002 | Owner: Jane Smith | Balance: $500.00
Account A003 | Owner: Bob Johnson | Balance: $250.00
Before deposit: Account A001 | Owner: John Doe | Balance: $1000.00
After deposit: Account A001 | Owner: John Doe | Balance: $1500.00
Before withdrawal: Account A002 | Owner: Jane Smith | Balance: $500.00
After withdrawal: Account A002 | Owner: Jane Smith | Balance: $300.00
Trying to create an existing account:
Account A001 already exists
Python First Bank - Managing 3 accounts
Este exemplo demonstra como usar a classe BankAccount que criamos anteriormente em uma aplicação mais completa. Ele mostra como os métodos especiais __init__, __str__ e __repr__ fornecem uma base sólida para criar classes intuitivas e fáceis de usar.
Resumo
Neste laboratório, você aprendeu sobre três métodos especiais essenciais em Python que ajudam a criar objetos mais expressivos e fáceis de usar:
__init__: O método construtor que inicializa os atributos de um objeto quando ele é criado.__str__: Um método que fornece uma representação de string legível por humanos de um objeto, principalmente para usuários finais.__repr__: Um método que fornece uma representação de string inequívoca de um objeto, principalmente para desenvolvedores e depuração.
Você implementou esses métodos em vários exemplos, desde classes simples como Person e Book até aplicações mais complexas como um sistema bancário. Você também aprendeu as diferenças entre __str__ e __repr__ e quando usar cada um.
Ao dominar esses métodos especiais, você pode criar objetos Python mais intuitivos e personalizados que se integram perfeitamente com os recursos embutidos da linguagem. Esse conhecimento forma uma parte essencial da programação orientada a objetos em Python e o ajudará a escrever um código mais limpo e de fácil manutenção.
Ao continuar sua jornada em Python, lembre-se de que existem muitos outros métodos especiais disponíveis que permitem personalizar ainda mais o comportamento de seus objetos. Explorar esses métodos lhe dará ainda mais controle sobre como seus objetos se comportam em diferentes contextos.



