はじめに
この実験では、ミックスインクラス(mixin classes)について学び、コードの再利用性を高める際におけるその役割を理解します。既存のコードを変更することなく、ミックスインを実装してクラスの機能を拡張する方法を学びます。
また、Python の協調的継承(cooperative inheritance)技術を習得します。実験中に tableformat.py
ファイルを変更します。
This tutorial is from open-source community. Access the source code
💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください
この実験では、ミックスインクラス(mixin classes)について学び、コードの再利用性を高める際におけるその役割を理解します。既存のコードを変更することなく、ミックスインを実装してクラスの機能を拡張する方法を学びます。
また、Python の協調的継承(cooperative inheritance)技術を習得します。実験中に tableformat.py
ファイルを変更します。
このステップでは、現在の表フォーマット実装における制限について調べます。また、この問題のいくつかの解決策を検討します。
まず、やることを理解しましょう。VSCode エディタを開き、プロジェクトディレクトリ内の tableformat.py
ファイルを見ます。このファイルは、表形式のデータをテキスト、CSV、HTML などのさまざまな形式でフォーマットするコードが含まれているため重要です。
ファイルを開くには、ターミナルで以下のコマンドを使用します。cd
コマンドはディレクトリをプロジェクトディレクトリに変更し、code
コマンドは VSCode で tableformat.py
ファイルを開きます。
cd ~/project
code tableformat.py
ファイルを開くと、いくつかのクラスが定義されていることに気づくでしょう。これらのクラスは、表データのフォーマットにおいて異なる役割を果たします。
TableFormatter
:これは抽象基底クラス(abstract base class)です。表の見出しと行をフォーマットするためのメソッドがあります。他のフォーマッタクラスの青写真と考えてください。TextTableFormatter
:このクラスは、表を平文形式で出力するために使用されます。CSVTableFormatter
:このクラスは、表データを CSV(Comma-Separated Values)形式でフォーマットする責任があります。HTMLTableFormatter
:このクラスは、表データを HTML 形式でフォーマットします。また、ファイルには print_table()
関数もあります。この関数は、先ほど述べたフォーマッタクラスを使用して表形式のデータを表示します。
では、いくつかの Python コードを実行して、これらのクラスがどのように動作するかを見てみましょう。ターミナルを開き、Python セッションを開始します。以下のコードは、tableformat.py
ファイルから必要な関数とクラスをインポートし、TextTableFormatter
オブジェクトを作成し、print_table()
関数を使用してポートフォリオデータを表示します。
python3 -c "
from tableformat import print_table, TextTableFormatter, portfolio
formatter = TextTableFormatter()
print_table(portfolio, ['name', 'shares', 'price'], formatter)
"
コードを実行すると、以下のような出力が表示されるはずです。
name shares price
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
では、問題を見つけましょう。price
列の値が一貫してフォーマットされていないことに注意してください。一部の値は 32.2 のように小数点以下 1 桁で、他の値は 51.23 のように小数点以下 2 桁です。金融データでは、通常、フォーマットを一貫させたいと思います。
以下は、出力を望む形式です。
name shares price
---------- ---------- ----------
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
これを修正する 1 つの方法は、print_table()
関数を変更してフォーマット指定を受け取るようにすることです。以下のコードは、これを行う方法を示しています。追加の formats
パラメータを受け取る新しい print_table()
関数を定義します。関数内で、これらのフォーマット指定を使用して行内の各値をフォーマットします。
python3 -c "
from tableformat import TextTableFormatter, portfolio
def print_table(records, fields, formats, formatter):
formatter.headings(fields)
for r in records:
rowdata = [(fmt % getattr(r, fieldname))
for fieldname, fmt in zip(fields, formats)]
formatter.row(rowdata)
formatter = TextTableFormatter()
print_table(portfolio,
['name','shares','price'],
['%s','%d','%0.2f'],
formatter)
"
この解決策は機能しますが、欠点があります。関数のインターフェースを変更すると、古いバージョンの print_table()
関数を使用している既存のコードが動作しなくなる可能性があります。
別のアプローチは、サブクラス化によってカスタムフォーマッタを作成することです。TextTableFormatter
を継承する新しいクラスを作成し、row()
メソッドをオーバーライドして、望むフォーマットを適用することができます。
python3 -c "
from tableformat import TextTableFormatter, print_table, portfolio
class PortfolioFormatter(TextTableFormatter):
def row(self, rowdata):
formats = ['%s','%d','%0.2f']
rowdata = [(fmt % d) for fmt, d in zip(formats, rowdata)]
super().row(rowdata)
formatter = PortfolioFormatter()
print_table(portfolio, ['name','shares','price'], formatter)
"
この解決策も機能しますが、あまり便利ではありません。異なるフォーマットが必要になるたびに、新しいクラスを作成する必要があります。また、今回の場合 TextTableFormatter
からサブクラス化しているため、特定のフォーマッタタイプに制限されます。
次のステップでは、ミックスインクラス(mixin classes)を使用したよりエレガントな解決策を探ります。
このステップでは、ミックスインクラス(mixin classes)について学びます。ミックスインクラスは Python で非常に便利なテクニックです。これを使うと、クラスの元のコードを変更することなく、追加の機能をクラスに追加できます。これは、コードをモジュール化して管理しやすくするのに役立つため、非常に優れています。
ミックスインは特殊なタイプのクラスです。その主な目的は、他のクラスが継承できる機能を提供することです。ただし、ミックスインは単独で使用することを想定していません。直接ミックスインクラスのインスタンスを作成することはありません。代わりに、特定の機能を他のクラスに制御可能かつ予測可能な方法で追加する手段として使用します。これは多重継承(multiple inheritance)の一種で、クラスが複数の親クラスから継承することができます。
では、tableformat.py
ファイルに 2 つのミックスインクラスを実装しましょう。まず、エディタでファイルを開きます。ターミナルで以下のコマンドを実行することで、これを行うことができます。
cd ~/project
code tableformat.py
ファイルが開いたら、既存の関数の前で、ファイルの末尾に以下のクラス定義を追加します。
class ColumnFormatMixin:
formats = []
def row(self, rowdata):
rowdata = [(fmt % d) for fmt, d in zip(self.formats, rowdata)]
super().row(rowdata)
この ColumnFormatMixin
クラスは、列フォーマット機能を提供します。formats
クラス変数は、フォーマットコードを保持するリストです。これらのコードは、各列のデータをフォーマットするために使用されます。row()
メソッドは行データを受け取り、行内の各要素にフォーマットコードを適用し、その後 super().row(rowdata)
を使用してフォーマットされた行データを親クラスに渡します。
次に、表のヘッダーを大文字で表示する別のミックスインクラスを追加します。
class UpperHeadersMixin:
def headings(self, headers):
super().headings([h.upper() for h in headers])
この UpperHeadersMixin
クラスは、ヘッダーテキストを大文字に変換します。ヘッダーのリストを受け取り、各ヘッダーを大文字に変換し、その後 super().headings()
を使用して変更されたヘッダーを親クラスの headings()
メソッドに渡します。
新しいミックスインクラスをテストしましょう。いくつかの Python コードを実行して、それらがどのように動作するかを確認します。
python3 -c "
from tableformat import TextTableFormatter, ColumnFormatMixin, portfolio, print_table
class PortfolioFormatter(ColumnFormatMixin, TextTableFormatter):
formats = ['%s', '%d', '%0.2f']
formatter = PortfolioFormatter()
print_table(portfolio, ['name','shares','price'], formatter)
"
このコードを実行すると、きれいにフォーマットされた出力が表示されるはずです。ColumnFormatMixin
が提供するフォーマットにより、価格列は小数点以下の桁数が一致します。
name shares price
---------- ---------- ----------
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
では、UpperHeadersMixin
を試してみましょう。以下のコードを実行します。
python3 -c "
from tableformat import TextTableFormatter, UpperHeadersMixin, portfolio, print_table
class PortfolioFormatter(UpperHeadersMixin, TextTableFormatter):
pass
formatter = PortfolioFormatter()
print_table(portfolio, ['name','shares','price'], formatter)
"
このコードは、ヘッダーを大文字で表示するはずです。
NAME SHARES PRICE
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
ミックスインクラスでは、super().method()
を使用していることに注意してください。これは「協調的継承(cooperative inheritance)」と呼ばれます。協調的継承では、継承チェーン内の各クラスが協力して動作します。クラスが super().method()
を呼び出すとき、それはチェーン内の次のクラスにタスクの一部を実行するように依頼しています。このように、クラスのチェーンはそれぞれ独自の動作を全体のプロセスに追加することができます。
継承の順序は非常に重要です。class PortfolioFormatter(ColumnFormatMixin, TextTableFormatter)
を定義するとき、Python はまず ColumnFormatMixin
でメソッドを探し、次に TextTableFormatter
で探します。したがって、ColumnFormatMixin
で super().row()
が呼び出されるとき、それは TextTableFormatter.row()
を指します。
両方のミックスインを組み合わせることもできます。以下のコードを実行します。
python3 -c "
from tableformat import TextTableFormatter, ColumnFormatMixin, UpperHeadersMixin, portfolio, print_table
class PortfolioFormatter(ColumnFormatMixin, UpperHeadersMixin, TextTableFormatter):
formats = ['%s', '%d', '%0.2f']
formatter = PortfolioFormatter()
print_table(portfolio, ['name','shares','price'], formatter)
"
このコードは、大文字のヘッダーとフォーマットされた数値の両方を提供します。
NAME SHARES PRICE
---------- ---------- ----------
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
次のステップでは、create_formatter()
関数を拡張することで、これらのミックスインを使いやすくします。
ミックスインは Python の強力な機能ですが、多重継承を伴うため、初心者には少し扱いにくい場合があります。多重継承はかなり複雑になることがあります。このステップでは、create_formatter()
関数を改良することで、ユーザーにとって使いやすいものにします。これにより、ユーザーは多重継承の詳細にあまり気を使う必要がなくなります。
まず、tableformat.py
ファイルを開く必要があります。ターミナルで以下のコマンドを実行することで、これを行うことができます。cd
コマンドはディレクトリをプロジェクトフォルダに変更し、code
コマンドはコードエディタで tableformat.py
ファイルを開きます。
cd ~/project
code tableformat.py
ファイルが開いたら、create_formatter()
関数を見つけます。現在、この関数は次のようになっています。
def create_formatter(name):
"""
Create an appropriate formatter based on the name.
"""
if name == 'text':
return TextTableFormatter()
elif name == 'csv':
return CSVTableFormatter()
elif name == 'html':
return HTMLTableFormatter()
else:
raise RuntimeError(f'Unknown format {name}')
この関数は名前を引数として受け取り、対応するフォーマッタを返します。しかし、もっと柔軟にするために、ミックスイン用のオプション引数を受け取れるように変更します。
既存の create_formatter()
関数を以下の拡張版に置き換えます。この新しい関数では、列フォーマットとヘッダーを大文字に変換するかどうかを指定できます。
def create_formatter(name, column_formats=None, upper_headers=False):
"""
Create a formatter with optional enhancements.
Parameters:
name : str
Name of the formatter ('text', 'csv', 'html')
column_formats : list, optional
List of format strings for column formatting
upper_headers : bool, optional
Whether to convert headers to uppercase
"""
if name == 'text':
formatter_cls = TextTableFormatter
elif name == 'csv':
formatter_cls = CSVTableFormatter
elif name == 'html':
formatter_cls = HTMLTableFormatter
else:
raise RuntimeError(f'Unknown format {name}')
## Apply mixins if requested
if column_formats and upper_headers:
class CustomFormatter(ColumnFormatMixin, UpperHeadersMixin, formatter_cls):
formats = column_formats
return CustomFormatter()
elif column_formats:
class CustomFormatter(ColumnFormatMixin, formatter_cls):
formats = column_formats
return CustomFormatter()
elif upper_headers:
class CustomFormatter(UpperHeadersMixin, formatter_cls):
pass
return CustomFormatter()
else:
return formatter_cls()
この拡張された関数は、まず name
引数に基づいて基本のフォーマッタクラスを決定します。次に、column_formats
と upper_headers
が指定されているかどうかに応じて、適切なミックスインを含むカスタムフォーマッタクラスを作成します。最後に、カスタムフォーマッタクラスのインスタンスを返します。
では、さまざまなオプションの組み合わせで拡張された関数をテストしましょう。
まず、列フォーマットを使用してみましょう。ターミナルで以下のコマンドを実行します。このコマンドは、tableformat.py
ファイルから必要な関数とデータをインポートし、列フォーマットを持つフォーマッタを作成し、そのフォーマッタを使用して表を印刷します。
python3 -c "
from tableformat import create_formatter, portfolio, print_table
formatter = create_formatter('text', column_formats=['%s', '%d', '%0.2f'])
print_table(portfolio, ['name', 'shares', 'price'], formatter)
"
列がフォーマットされた表が表示されるはずです。出力は次のようになります。
name shares price
---------- ---------- ----------
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
次に、大文字のヘッダーを使用してみましょう。以下のコマンドを実行します。
python3 -c "
from tableformat import create_formatter, portfolio, print_table
formatter = create_formatter('text', upper_headers=True)
print_table(portfolio, ['name', 'shares', 'price'], formatter)
"
大文字のヘッダーを持つ表が表示されるはずです。出力は次のようになります。
NAME SHARES PRICE
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
最後に、両方のオプションを組み合わせてみましょう。このコマンドを実行します。
python3 -c "
from tableformat import create_formatter, portfolio, print_table
formatter = create_formatter('text', column_formats=['%s', '%d', '%0.2f'], upper_headers=True)
print_table(portfolio, ['name', 'shares', 'price'], formatter)
"
これにより、列がフォーマットされ、ヘッダーが大文字の表が表示されるはずです。出力は次のようになります。
NAME SHARES PRICE
---------- ---------- ----------
AA 100 32.20
IBM 50 91.10
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.10
IBM 100 70.44
拡張された関数は、他のフォーマッタタイプでも機能します。たとえば、CSV フォーマッタで試してみましょう。以下のコマンドを実行します。
python3 -c "
from tableformat import create_formatter, portfolio, print_table
formatter = create_formatter('csv', column_formats=['\\"%s\\"', '%d', '%0.2f'])
print_table(portfolio, ['name', 'shares', 'price'], formatter)
"
これにより、列がフォーマットされた CSV 出力が生成されるはずです。出力は次のようになります。
name,shares,price
"AA",100,32.20
"IBM",50,91.10
"CAT",150,83.44
"MSFT",200,51.23
"GE",95,40.37
"MSFT",50,65.10
"IBM",100,70.44
create_formatter()
関数を拡張することで、使いやすい API を作成しました。ユーザーは今では、多重継承の複雑な詳細を理解することなく、簡単にミックスインを使用できます。これにより、ユーザーは自分のニーズに合わせてフォーマッタをカスタマイズする柔軟性が得られます。
この実験では、Python のミックスインクラスと協調的継承について学びました。これらは、既存のコードを変更することなくクラスの機能を拡張するための強力なテクニックです。単一継承の制限を理解すること、特定の機能のためのミックスインクラスを作成すること、協調的継承のために super()
を使用してメソッドチェーンを構築することなどの重要な概念を探りました。
これらのテクニックは、特にフレームワークやライブラリにおいて、保守可能で拡張可能な Python コードを書くために非常に価値があります。これらにより、ユーザーが既存のコードを書き換える必要なくカスタマイズポイントを提供でき、複数のミックスインを組み合わせて複雑な動作を構成することができます。また、ユーザーに優しい API で継承の複雑さを隠すことも可能です。