はじめに
Python のオブジェクト指向プログラミング (OOP) 機能は、表現力豊かでカスタマイズされたオブジェクトを作成するための強力なツールを提供します。その中でも、特殊メソッドである __init__, __str__, および __repr__ は、Python オブジェクトの動作と表現を定義する上で重要な役割を果たします。
このチュートリアルでは、これらの特殊メソッドを探求し、その目的を理解し、Python クラスで効果的に実装する方法を学びます。この実験 (Lab) の終わりには、より直感的で使いやすい Python オブジェクトを作成できるようになります。
__init__ メソッドを使用したクラスの作成
最初に探求する特殊メソッドは __init__ で、オブジェクトが作成されたときに呼び出されます。このメソッドを使用すると、オブジェクトの属性を初期化できます。
__init__ メソッドの理解
__init__ メソッドは、コンストラクタとしても知られており、クラスの新しいインスタンスを作成すると自動的に呼び出されます。その主な目的は、属性に値を割り当てることによって、オブジェクトの初期状態を設定することです。
__init__ メソッドを使用して、シンプルな Python クラスを作成することから始めましょう。
実験 (LabEx) 環境でターミナルを開きます。
プロジェクトディレクトリに移動します。
cd ~/projectコードエディタを使用して、
person.pyという新しい Python ファイルを作成します。エディタで、
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}")ファイルを保存します。
Python スクリプトを実行します。
python3 person.py
次の出力が表示されるはずです。
Name: Alice
Age: 30
__init__ メソッドの説明
上記のコードでは、
nameとageの 3 つのパラメータを受け取る__init__メソッドを持つPersonクラスを定義しました。selfパラメータは、作成中のインスタンスを参照し、オブジェクトが作成されると自動的に渡されます。__init__メソッド内で、nameとageの値をself.nameとself.ageを使用してオブジェクトの属性に割り当てます。person1 = Person("Alice", 30)で新しいPersonオブジェクトを作成すると、__init__メソッドが自動的に呼び出されます。- その後、ドット表記を使用して属性にアクセスできます:
person1.nameとperson1.age。
より多くの機能の追加
現在の年と個人の年齢に基づいて生年を計算するメソッドを追加して、Person クラスを強化しましょう。
エディタで
person.pyを開きます。生年を計算するメソッドを含めるようにコードを更新します。
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()}")ファイルを保存します。
Python スクリプトを実行します。
python3 person.py
出力には、計算された生年も含まれるようになりました。
Name: Alice
Age: 30
Birth Year: 1993
(実際の生年は、スクリプトを実行した現在の年によって異なります)
これで、オブジェクトの属性を初期化するための __init__ メソッドを持つ Python クラスを作成し、それらの属性に基づいて計算を実行するメソッドを追加しました。
__str__ メソッドの実装
__init__ メソッドでクラスを作成する方法を理解したので、__str__ と呼ばれる別の特殊メソッドを探求しましょう。このメソッドを使用すると、オブジェクトを文字列としてどのように表現するかを定義できます。
__str__ メソッドの理解
__str__ メソッドは、オブジェクトに対して str() 関数を使用した場合、または print() 関数を使用してオブジェクトを出力した場合に呼び出されます。オブジェクトの人間が読める文字列表現を返す必要があります。
__str__ メソッドを含めるように Person クラスを更新しましょう。
エディタで
person.pyを開きます。__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))ファイルを保存します。
Python スクリプトを実行します。
python3 person.py
次のような出力が表示されるはずです。
Name: Alice
Age: 30
Birth Year: 1993
String representation of the object:
Alice, 30 years old
Explicit string conversion:
Alice, 30 years old
__str__ の仕組み
上記のコードでは、
- 人の名前と年齢を含むフォーマットされた文字列を返す
__str__メソッドを定義しました。 print(person1)を呼び出すと、Python は自動的に__str__メソッドを呼び出して、何を表示するかを決定します。str(person1)を使用してオブジェクトを明示的に文字列に変換することもできます。これも__str__メソッドを呼び出します。
__str__ がない場合どうなるか
__str__ メソッドの重要性を理解するために、それを定義しない場合に何が起こるかを見てみましょう。
without_str.pyという新しいファイルを作成します。次のコードを追加します。
class SimpleClass: def __init__(self, value): self.value = value ## Create an object obj = SimpleClass(42) ## Print the object print(obj)ファイルを保存します。
スクリプトを実行します。
python3 without_str.py
次のような出力が表示されるはずです。
<__main__.SimpleClass object at 0x7f2d8c3e9d90>
出力はあまり情報を提供していません。クラス名とオブジェクトのメモリ アドレスが表示されますが、その内容は表示されません。これが、オブジェクトをより使いやすくするために、適切な __str__ メソッドを実装することが重要な理由です。
実践的な演習
__str__ メソッドを持つ Book という新しいクラスを作成しましょう。
book.pyという新しいファイルを作成します。次のコードを追加します。
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)ファイルを保存します。
スクリプトを実行します。
python3 book.py
出力は次のようになります。
"The Great Gatsby" by F. Scott Fitzgerald (180 pages)
"To Kill a Mockingbird" by Harper Lee (281 pages)
これで、__str__ メソッドを使用して、オブジェクトの人間が読める文字列表現を作成する方法を理解できました。
__repr__ メソッドの実装
__str__ に加えて、Python は文字列表現のための別の特殊メソッドである __repr__ を提供しています。__str__ が人間が読める表現を提供することを目的としているのに対し、__repr__ は、可能であればオブジェクトを再作成するために使用できる、オブジェクトの明確な表現を提供することを目的としています。
__repr__ メソッドの理解
__repr__ メソッドは、オブジェクトに対して repr() 関数を使用した場合、または対話型セッションでオブジェクトを表示した場合に呼び出されます。eval() に渡すと、同等のオブジェクトを作成する文字列を返す必要があります (可能な場合)。
__repr__ メソッドを含めるように Book クラスを更新しましょう。
エディタで
book.pyを開きます。__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))ファイルを保存します。
スクリプトを実行します。
python3 book.py
次のような出力が表示されるはずです。
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)
__str__ と __repr__ の違い
__str__ と __repr__ の主な違いは次のとおりです。
__str__は人間が読める出力を目的としていますが、__repr__は開発者とデバッグを目的としています。__str__が定義されておらず、__repr__が定義されている場合、Python はstr()またはprint()のフォールバックとして__repr__を使用します。__repr__は、理想的には、eval()に渡されたときにオブジェクトを再作成できる文字列を返す必要がありますが、これは常に可能または必要とは限りません。
__repr__ を使用した eval() 関数
正しく実装されている場合、__repr__ によって返される文字列は、eval() でオブジェクトを再作成するために使用できます。これを実際に見てみましょう。
repr_eval.pyという新しいファイルを作成します。次のコードを追加します。
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}")ファイルを保存します。
スクリプトを実行します。
python3 repr_eval.py
次のような出力が表示されるはずです。
Representation: Point(3, 4)
Recreated object: Point at (3, 4)
p1.x = 3, p1.y = 4
p2.x = 3, p2.y = 4
これは、__repr__ によって返される文字列と eval() 関数を使用して、元のオブジェクトを再作成できることを示しています。
各メソッドを使用するタイミング
- オブジェクトの初期状態を設定するには、
__init__を使用します。 - エンドユーザー向けに人間が読める表現を提供するには、
__str__を使用します。 - 開発者とデバッグ向けに、正確で明確な表現を提供するには、
__repr__を使用します。
実践的な演習:完全な例
3 つの特殊メソッドすべてを使用して Rectangle クラスを作成することにより、すべてをまとめましょう。
rectangle.pyという新しいファイルを作成します。次のコードを追加します。
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()}")ファイルを保存します。
スクリプトを実行します。
python3 rectangle.py
次のような出力が表示されるはずです。
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
この例は、3 つの特殊メソッド (__init__、__str__、および repr`) が連携して、適切に設計されたクラスを作成する方法を示しています。
実用的なアプリケーションの作成
__init__、__str__、 および __repr__ の 3 つの特殊メソッドを理解したので、それらの使用を実際のシナリオで示す、より実用的なアプリケーションを作成しましょう。BankAccount クラスを使用して、シンプルな銀行システムを作成します。
銀行口座クラスの構築
bank_account.pyという新しいファイルを作成します。次のコードを追加します。
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})'ファイルを保存します。
銀行口座クラスのテスト
次に、BankAccount クラスをテストしましょう。
bank_test.pyという新しいファイルを作成します。次のコードを追加します。
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}")ファイルを保存します。
スクリプトを実行します。
python3 bank_test.py
次のような出力が表示されるはずです。
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
実装の理解
この銀行アプリケーションでは、
__init__メソッドは、口座番号、所有者名、およびオプションの初期残高で銀行口座を初期化します。また、トランザクションを追跡するための空のリストも作成します。depositおよびwithdrawメソッドは、トランザクションを処理し、残高とトランザクション履歴を更新します。__str__メソッドは、口座番号、所有者名、および現在の残高を示す、ユーザーフレンドリーな口座表現を提供します。__repr__メソッドは、口座オブジェクトを再作成できる文字列を提供します (ただし、この実装ではトランザクション履歴は保持されないことに注意してください)。
この例は、これらの特殊メソッドを実用的なアプリケーションで使用して、より直感的でユーザーフレンドリーなオブジェクトを作成する方法を示しています。
アプリケーションの拡張
最後の演習として、複数の口座を管理するシンプルな銀行システムを作成しましょう。
banking_system.pyという新しいファイルを作成します。次のコードを追加します。
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}")ファイルを保存します。
スクリプトを実行します。
python3 banking_system.py
次のような出力が表示されるはずです。
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
この例は、以前に作成した BankAccount クラスを、より完全なアプリケーションで使用する方法を示しています。特殊メソッド __init__、__str__、 および __repr__ が、直感的でユーザーフレンドリーなクラスを作成するための強固な基盤を提供する方法を示しています。
まとめ
この実験では、より表現力豊かでユーザーフレンドリーなオブジェクトを作成するのに役立つ、Python の 3 つの重要な特殊メソッドについて学びました。
__init__: オブジェクトが作成されたときに、オブジェクトの属性を初期化するコンストラクターメソッドです。__str__: 主にエンドユーザー向けに、オブジェクトの人間が読める文字列表現を提供するメソッドです。__repr__: 主に開発者とデバッグ向けに、オブジェクトの明確な文字列表現を提供するメソッドです。
これらのメソッドを、Person や Book などのシンプルなクラスから、銀行システムなどのより複雑なアプリケーションまで、いくつかの例で実装しました。また、__str__ と __repr__ の違いと、それぞれをいつ使用するかについても学びました。
これらの特殊メソッドを習得することで、言語の組み込み機能とシームレスに統合される、より直感的でカスタマイズされた Python オブジェクトを作成できます。この知識は、Python でのオブジェクト指向プログラミングの不可欠な部分を形成し、よりクリーンで保守性の高いコードを記述するのに役立ちます。
Python の旅を続ける中で、オブジェクトの動作をさらにカスタマイズできる、他の多くの特殊メソッドがあることを覚えておいてください。これらのメソッドを探索することで、さまざまなコンテキストでオブジェクトがどのように動作するかをさらに制御できるようになります。



