はじめに
この実験(Lab)では、private 属性を使用してオブジェクトの内部をカプセル化し、属性アクセスを制御するために property デコレーターを実装する方法を学びます。これらのテクニックは、オブジェクトの整合性を維持し、適切なデータ処理を保証するために不可欠です。
また、__slots__
を使用して属性の作成を制限する方法も理解します。この実験(Lab)全体を通して stock.py
ファイルを修正し、これらの概念を適用します。
This tutorial is from open-source community. Access the source code
💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください
この実験(Lab)では、private 属性を使用してオブジェクトの内部をカプセル化し、属性アクセスを制御するために property デコレーターを実装する方法を学びます。これらのテクニックは、オブジェクトの整合性を維持し、適切なデータ処理を保証するために不可欠です。
また、__slots__
を使用して属性の作成を制限する方法も理解します。この実験(Lab)全体を通して stock.py
ファイルを修正し、これらの概念を適用します。
Python では、属性がクラス内で内部的に使用されることを示すために、命名規則を使用します。これらの属性にはアンダースコア (_
) をプレフィックスとして付けます。これは、これらの属性がパブリック API の一部ではなく、クラスの外部から直接アクセスすべきではないことを他の開発者に知らせるものです。
stock.py
ファイルの現在の Stock
クラスを見てみましょう。types
という名前のクラス変数があります。
class Stock:
## Class variable for type conversions
types = (str, int, float)
## Rest of the class...
types
クラス変数は、行データを変換するために内部的に使用されます。これが実装の詳細であることを示すために、private としてマークします。
手順:
エディターで stock.py
ファイルを開きます。
types
クラス変数を変更して、先頭にアンダースコアを追加し、_types
に変更します。
class Stock:
## Class variable for type conversions
_types = (str, int, float)
## Rest of the class...
from_row
メソッドを更新して、名前を変更した変数 _types
を使用します。
@classmethod
def from_row(cls, row):
values = [func(val) for func, val in zip(cls._types, row)]
return cls(*values)
stock.py
ファイルを保存します。
test_stock.py
という名前の Python スクリプトを作成して、変更をテストします。次のコマンドを使用して、エディターでファイルを作成できます。
touch /home/labex/project/test_stock.py
次のコードを test_stock.py
ファイルに追加します。このコードは、Stock
クラスのインスタンスを作成し、それらに関する情報を出力します。
from stock import Stock
## Create a stock instance
s = Stock('GOOG', 100, 490.10)
print(f"Name: {s.name}, Shares: {s.shares}, Price: {s.price}")
print(f"Cost: {s.cost()}")
## Create from row
row = ['AAPL', '50', '142.5']
apple = Stock.from_row(row)
print(f"Name: {apple.name}, Shares: {apple.shares}, Price: {apple.price}")
print(f"Cost: {apple.cost()}")
ターミナルで次のコマンドを使用して、テストスクリプトを実行します。
python /home/labex/project/test_stock.py
次のような出力が表示されるはずです。
Name: GOOG, Shares: 100, Price: 490.1
Cost: 49010.0
Name: AAPL, Shares: 50, Price: 142.5
Cost: 7125.0
Python のプロパティを使用すると、計算された値を属性のようにアクセスできます。これにより、メソッドを呼び出すときに括弧が不要になり、コードがよりクリーンで一貫性のあるものになります。
現在、Stock
クラスには、株式の総コストを計算する cost()
メソッドがあります。
def cost(self):
return self.shares * self.price
コスト値を取得するには、括弧を付けて呼び出す必要があります。
s = Stock('GOOG', 100, 490.10)
print(s.cost()) ## Calls the method
cost()
メソッドをプロパティに変換することで、これを改善し、括弧なしでコスト値にアクセスできるようにすることができます。
s = Stock('GOOG', 100, 490.10)
print(s.cost) ## Accesses the property
手順:
エディターで stock.py
ファイルを開きます。
@property
デコレーターを使用して、cost()
メソッドをプロパティに置き換えます。
@property
def cost(self):
return self.shares * self.price
stock.py
ファイルを保存します。
エディターで test_property.py
という名前の新しいファイルを作成します。
touch /home/labex/project/test_property.py
次のコードを test_property.py
ファイルに追加して、Stock
インスタンスを作成し、cost
プロパティにアクセスします。
from stock import Stock
## Create a stock instance
s = Stock('GOOG', 100, 490.10)
## Access cost as a property (no parentheses)
print(f"Stock: {s.name}")
print(f"Shares: {s.shares}")
print(f"Price: {s.price}")
print(f"Cost: {s.cost}") ## Using the property
テストスクリプトを実行します。
python /home/labex/project/test_property.py
次のような出力が表示されるはずです。
Stock: GOOG
Shares: 100
Price: 490.1
Cost: 49010.0
プロパティを使用すると、属性値の取得、設定、および削除の方法を制御することもできます。これは、属性に検証を追加し、値が特定の基準を満たしていることを確認するのに役立ちます。
Stock
クラスでは、shares
が負でない整数であり、price
が負でない浮動小数点数であることを確認する必要があります。これを実現するために、getter(ゲッター)と setter(セッター)とともにプロパティデコレーターを使用します。
手順:
エディターで stock.py
ファイルを開きます。
private 属性 _shares
と _price
を Stock
クラスに追加し、コンストラクター(constructor)を変更してそれらを使用します。
def __init__(self, name, shares, price):
self.name = name
self._shares = shares ## Using private attribute
self._price = price ## Using private attribute
検証を使用して shares
と price
のプロパティを定義します。
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError("Expected integer")
if value < 0:
raise ValueError("shares must be >= 0")
self._shares = value
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if not isinstance(value, float):
raise TypeError("Expected float")
if value < 0:
raise ValueError("price must be >= 0")
self._price = value
コンストラクターを更新して、検証のためにプロパティセッターを使用します。
def __init__(self, name, shares, price):
self.name = name
self.shares = shares ## Using property setter
self.price = price ## Using property setter
stock.py
ファイルを保存します。
test_validation.py
という名前のテストスクリプトを作成します。
touch /home/labex/project/test_validation.py
次のコードを test_validation.py
ファイルに追加します。
from stock import Stock
## Create a valid stock instance
s = Stock('GOOG', 100, 490.10)
print(f"Initial: Name={s.name}, Shares={s.shares}, Price={s.price}, Cost={s.cost}")
## Test valid updates
try:
s.shares = 50 ## Valid update
print(f"After setting shares=50: Shares={s.shares}, Cost={s.cost}")
except Exception as e:
print(f"Error setting shares=50: {e}")
try:
s.price = 123.45 ## Valid update
print(f"After setting price=123.45: Price={s.price}, Cost={s.cost}")
except Exception as e:
print(f"Error setting price=123.45: {e}")
## Test invalid updates
try:
s.shares = "50" ## Invalid type (string)
print("This line should not execute")
except Exception as e:
print(f"Error setting shares='50': {e}")
try:
s.shares = -10 ## Invalid value (negative)
print("This line should not execute")
except Exception as e:
print(f"Error setting shares=-10: {e}")
try:
s.price = "123.45" ## Invalid type (string)
print("This line should not execute")
except Exception as e:
print(f"Error setting price='123.45': {e}")
try:
s.price = -10.0 ## Invalid value (negative)
print("This line should not execute")
except Exception as e:
print(f"Error setting price=-10.0: {e}")
テストスクリプトを実行します。
python /home/labex/project/test_validation.py
有効な更新が成功し、無効な更新に対して適切なエラーメッセージが表示される出力が表示されるはずです。
Initial: Name=GOOG, Shares=100, Price=490.1, Cost=49010.0
After setting shares=50: Shares=50, Cost=24505.0
After setting price=123.45: Price=123.45, Cost=6172.5
Error setting shares='50': Expected integer
Error setting shares=-10: shares must be >= 0
Error setting price='123.45': Expected float
Error setting price=-10.0: price must be >= 0
__slots__
の使用__slots__
属性は、クラスが持つことができる属性を制限します。インスタンスへの新しい属性の追加を防ぎ、メモリ使用量を削減します。
Stock
クラスでは、__slots__
を使用して以下を行います。
手順:
エディターで stock.py
ファイルを開きます。
__slots__
クラス変数(class variable)を追加し、クラスで使用されるすべての private 属性名をリストします。
class Stock:
## Class variable for type conversions
_types = (str, int, float)
## Define slots to restrict attribute creation
__slots__ = ('name', '_shares', '_price')
## Rest of the class...
ファイルを保存します。
test_slots.py
という名前のテストスクリプトを作成します。
touch /home/labex/project/test_slots.py
次のコードを test_slots.py
ファイルに追加します。
from stock import Stock
## Create a stock instance
s = Stock('GOOG', 100, 490.10)
## Access existing attributes
print(f"Name: {s.name}")
print(f"Shares: {s.shares}")
print(f"Price: {s.price}")
print(f"Cost: {s.cost}")
## Try to add a new attribute
try:
s.extra = "This will fail"
print(f"Extra: {s.extra}")
except AttributeError as e:
print(f"Error: {e}")
テストスクリプトを実行します。
python /home/labex/project/test_slots.py
定義された属性にアクセスできるものの、新しい属性を追加しようとすると AttributeError
が発生することが出力に表示されるはずです。
Name: GOOG
Shares: 100
Price: 490.1
Cost: 49010.0
Error: 'Stock' object has no attribute 'extra'
現在、Stock
クラスは、型処理のために _types
クラス変数とプロパティセッターの両方を使用しています。一貫性と保守性を向上させるために、これらのメカニズムを調整して、同じ型情報を使用するようにします。
手順:
エディターで stock.py
ファイルを開きます。
_types
クラス変数で定義された型を使用するようにプロパティセッターを変更します。
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, self._types[1]):
raise TypeError(f"Expected {self._types[1].__name__}")
if value < 0:
raise ValueError("shares must be >= 0")
self._shares = value
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if not isinstance(value, self._types[2]):
raise TypeError(f"Expected {self._types[2].__name__}")
if value < 0:
raise ValueError("price must be >= 0")
self._price = value
stock.py
ファイルを保存します。
test_subclass.py
という名前のテストスクリプトを作成します。
touch /home/labex/project/test_subclass.py
次のコードを test_subclass.py
ファイルに追加します。
from stock import Stock
from decimal import Decimal
## Create a subclass with different types
class DStock(Stock):
_types = (str, int, Decimal)
## Test the base class
s = Stock('GOOG', 100, 490.10)
print(f"Stock: {s.name}, Shares: {s.shares}, Price: {s.price}, Cost: {s.cost}")
## Test valid update with float
try:
s.price = 500.25
print(f"Updated Stock price: {s.price}, Cost: {s.cost}")
except Exception as e:
print(f"Error updating Stock price: {e}")
## Test the subclass with Decimal
ds = DStock('AAPL', 50, Decimal('142.50'))
print(f"DStock: {ds.name}, Shares: {ds.shares}, Price: {ds.price}, Cost: {ds.cost}")
## Test invalid update with float (should require Decimal)
try:
ds.price = 150.75
print(f"Updated DStock price: {ds.price}")
except Exception as e:
print(f"Error updating DStock price: {e}")
## Test valid update with Decimal
try:
ds.price = Decimal('155.25')
print(f"Updated DStock price: {ds.price}, Cost: {ds.cost}")
except Exception as e:
print(f"Error updating DStock price: {e}")
テストスクリプトを実行します。
python /home/labex/project/test_subclass.py
基本の Stock
クラスは price(価格)に float(浮動小数点数)値を受け入れますが、DStock
サブクラスは Decimal
値を必要とすることがわかります。
Stock: GOOG, Shares: 100, Price: 490.1, Cost: 49010.0
Updated Stock price: 500.25, Cost: 50025.0
DStock: AAPL, Shares: 50, Price: 142.50, Cost: 7125.00
Error updating DStock price: Expected Decimal
Updated DStock price: 155.25, Cost: 7762.50
この実験(Lab)では、private 属性の使用方法、メソッドをプロパティに変換する方法、プロパティの検証の実装方法、メモリ最適化のための __slots__
の使用方法、およびクラス変数との型検証の調整方法を学びました。これらのテクニックは、カプセル化を強制し、明確なインターフェースを提供することにより、クラスの堅牢性、効率、および保守性を向上させます。