Pythonにおける__init__、__str__、および__repr__メソッドの使い方

PythonPythonBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

Introduction

Python's object-oriented programming (OOP) features provide powerful tools to create expressive and customized objects. Among these, the special methods __init__, __str__, and __repr__ play crucial roles in defining the behavior and representation of your Python objects.

In this tutorial, we will explore these special methods, understand their purposes, and learn how to implement them effectively in your Python classes. By the end of this lab, you will be able to create more intuitive and user-friendly Python objects.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/constructor("Constructor") subgraph Lab Skills python/function_definition -.-> lab-415189{{"Pythonにおける__init__、__str__、および__repr__メソッドの使い方"}} python/classes_objects -.-> lab-415189{{"Pythonにおける__init__、__str__、および__repr__メソッドの使い方"}} python/constructor -.-> lab-415189{{"Pythonにおける__init__、__str__、および__repr__メソッドの使い方"}} end

Creating Classes with the __init__ Method

The first special method we will explore is __init__, which is called when an object is created. This method allows you to initialize the attributes of your object.

Understanding the __init__ Method

The __init__ method, also known as a constructor, is automatically called when you create a new instance of a class. Its primary purpose is to set up the initial state of the object by assigning values to attributes.

Let's start by creating a simple Python class with the __init__ method:

  1. Open the terminal in your LabEx environment.

  2. Navigate to the project directory:

    cd ~/project
  3. Create a new Python file called person.py using the code editor:

  4. In the editor, add the following code to 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}")
  5. Save the file.

  6. Run the Python script:

    python3 person.py

You should see the following output:

Name: Alice
Age: 30

Explanation of the __init__ Method

In the code above:

  • We defined a Person class with an __init__ method that takes three parameters: self, name, and age.
  • The self parameter refers to the instance being created and is automatically passed when an object is created.
  • Inside the __init__ method, we assign the values of name and age to the object's attributes using self.name and self.age.
  • When we create a new Person object with person1 = Person("Alice", 30), the __init__ method is automatically called.
  • We can then access the attributes using dot notation: person1.name and person1.age.

Adding More Functionality

Let's enhance our Person class by adding a method to calculate the birth year based on the current year and the person's age:

  1. Open person.py in the editor.

  2. Update the code to include a method for calculating the birth year:

    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()}")
  3. Save the file.

  4. Run the Python script:

    python3 person.py

The output should now include the calculated birth year:

Name: Alice
Age: 30
Birth Year: 1993

(The actual birth year will depend on the current year when you run the script)

Now you have created a Python class with the __init__ method to initialize object attributes and added a method to perform calculations based on those attributes.

Implementing the __str__ Method

Now that we understand how to create a class with the __init__ method, let's explore another special method called __str__. This method allows us to define how an object should be represented as a string.

Understanding the __str__ Method

The __str__ method is called when you use the str() function on an object or when you print an object using the print() function. It should return a human-readable string representation of the object.

Let's update our Person class to include a __str__ method:

  1. Open person.py in the editor.

  2. Update the code to include a __str__ method:

    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))
  3. Save the file.

  4. Run the Python script:

    python3 person.py

You should see output similar to this:

Name: Alice
Age: 30
Birth Year: 1993

String representation of the object:
Alice, 30 years old

Explicit string conversion:
Alice, 30 years old

How __str__ Works

In the code above:

  • We defined a __str__ method that returns a formatted string with the person's name and age.
  • When we call print(person1), Python automatically calls the __str__ method to determine what to display.
  • We can also explicitly convert an object to a string using str(person1), which also calls the __str__ method.

What Happens Without __str__

To understand the importance of the __str__ method, let's see what happens when we don't define it:

  1. Create a new file called without_str.py:

  2. Add the following code:

    class SimpleClass:
        def __init__(self, value):
            self.value = value
    
    ## Create an object
    obj = SimpleClass(42)
    
    ## Print the object
    print(obj)
  3. Save the file.

  4. Run the script:

    python3 without_str.py

You should see output like:

<__main__.SimpleClass object at 0x7f2d8c3e9d90>

The output is not very informative. It shows the class name and the memory address of the object, but not its content. This is why implementing a proper __str__ method is important for making your objects more user-friendly.

Practical Exercise

Let's create a new class called Book with a __str__ method:

  1. Create a new file called book.py:

  2. Add the following code:

    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)
  3. Save the file.

  4. Run the script:

    python3 book.py

The output should be:

"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)

Now you understand how to use the __str__ method to create human-readable string representations of your objects.

Implementing the __repr__ Method

In addition to __str__, Python provides another special method for string representation: __repr__. While __str__ is meant to provide a human-readable representation, __repr__ is intended to provide an unambiguous representation of an object that can be used to recreate the object if possible.

Understanding the __repr__ Method

The __repr__ method is called when you use the repr() function on an object or when you display an object in an interactive session. It should return a string that, when passed to eval(), would create an equivalent object (when possible).

Let's update our Book class to include a __repr__ method:

  1. Open book.py in the editor.

  2. Update the code to include a __repr__ method:

    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))
  3. Save the file.

  4. Run the script:

    python3 book.py

You should see output like:

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)

Differences Between __str__ and __repr__

The main differences between __str__ and __repr__ are:

  • __str__ is meant for human-readable output, while __repr__ is meant for developers and debugging.
  • If __str__ is not defined but __repr__ is, Python will use __repr__ as a fallback for str() or print().
  • __repr__ should ideally return a string that can recreate the object when passed to eval(), although this is not always possible or necessary.

The eval() Function with __repr__

When implemented correctly, the string returned by __repr__ can be used with eval() to recreate the object. Let's see this in action:

  1. Create a new file called repr_eval.py:

  2. Add the following code:

    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}")
  3. Save the file.

  4. Run the script:

    python3 repr_eval.py

You should see output like:

Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4

This demonstrates that we can recreate the original object using the string returned by __repr__ and the eval() function.

When to Use Each Method

  • Use __init__ to set up the initial state of your objects.
  • Use __str__ to provide a human-readable representation for end-users.
  • Use __repr__ to provide a precise, unambiguous representation for developers and debugging.

Practical Exercise: A Complete Example

Let's put it all together by creating a Rectangle class with all three special methods:

  1. Create a new file called rectangle.py:

  2. Add the following code:

    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()}")
  3. Save the file.

  4. Run the script:

    python3 rectangle.py

You should see output like:

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

This example demonstrates how all three special methods (__init__, __str__, and __repr__) work together to create a well-designed class.

Creating a Practical Application

Now that we understand the three special methods __init__, __str__, and __repr__, let's create a more practical application that demonstrates their use in a real-world scenario. We will create a simple banking system with a BankAccount class.

Building a Bank Account Class

  1. Create a new file called bank_account.py:

  2. Add the following code:

    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})'
  3. Save the file.

Testing the Bank Account Class

Now let's test our BankAccount class:

  1. Create a new file called bank_test.py:

  2. Add the following code:

    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}")
  3. Save the file.

  4. Run the script:

    python3 bank_test.py

You should see output similar to this:

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

Understanding the Implementation

In this banking application:

  1. The __init__ method initializes a bank account with an account number, owner name, and an optional initial balance. It also creates an empty list to track transactions.

  2. The deposit and withdraw methods handle transactions and update the balance and transaction history.

  3. The __str__ method provides a user-friendly representation of the account, showing the account number, owner name, and current balance.

  4. The __repr__ method provides a string that can recreate the account object (although note that the transaction history is not preserved in this implementation).

This example demonstrates how these special methods can be used in a practical application to create more intuitive and user-friendly objects.

Extending the Application

As a final exercise, let's create a simple banking system that manages multiple accounts:

  1. Create a new file called banking_system.py:

  2. Add the following code:

    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}")
  3. Save the file.

  4. Run the script:

    python3 banking_system.py

You should see output similar to:

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

This example demonstrates how to use the BankAccount class we created earlier in a more complete application. It shows how the special methods __init__, __str__, and __repr__ provide a solid foundation for creating intuitive and user-friendly classes.

Summary

In this lab, you learned about three essential special methods in Python that help you create more expressive and user-friendly objects:

  1. __init__: The constructor method that initializes an object's attributes when it's created.

  2. __str__: A method that provides a human-readable string representation of an object, primarily for end-users.

  3. __repr__: A method that provides an unambiguous string representation of an object, primarily for developers and debugging.

You implemented these methods in several examples, from simple classes like Person and Book to more complex applications like a banking system. You also learned the differences between __str__ and __repr__ and when to use each one.

By mastering these special methods, you can create more intuitive and customized Python objects that integrate seamlessly with the language's built-in features. This knowledge forms an essential part of object-oriented programming in Python and will help you write cleaner, more maintainable code.

As you continue your Python journey, remember that there are many other special methods available that allow you to customize the behavior of your objects even further. Exploring these methods will give you even more control over how your objects behave in different contexts.