組み込みメソッドデコレータの紹介

PythonPythonBeginner
オンラインで実践に進む

This tutorial is from open-source community. Access the source code

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

はじめに

このセクションでは、メソッド定義と組み合わせて使用されるいくつかの組み込みデコレータについて説明します。

事前定義済みのデコレータ

クラス定義において特別な種類のメソッドを指定するために使用される事前定義済みのデコレータがあります。

class Foo:
    def bar(self,a):
     ...

    @staticmethod
    def spam(a):
     ...

    @classmethod
    def grok(cls,a):
     ...

    @property
    def name(self):
     ...

順に見ていきましょう。

静的メソッド

@staticmethod は、いわゆる 静的 クラスメソッド(C++/Java 由来)を定義するために使用されます。静的メソッドは、クラスの一部である関数ですが、インスタンスに対しては動作しません。

class Foo(object):
    @staticmethod
    def bar(x):
        print('x =', x)

>>> Foo.bar(2) ## x=2
>>>

静的メソッドは、時々、クラスの内部サポートコードを実装するために使用されます。たとえば、作成されたインスタンスを管理するコード(メモリ管理、システムリソース、永続化、ロックなど)です。また、特定のデザインパターン(ここでは説明しません)によっても使用されます。

クラスメソッド

@classmethod は、クラスメソッドを定義するために使用されます。クラスメソッドは、インスタンスではなく、クラス オブジェクトを最初のパラメータとして受け取るメソッドです。

class Foo:
    def bar(self):
        print(self)
    @classmethod
    def spam(cls):
        print(cls)

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   ## インスタンス `f`
>>> Foo.spam()
<class '__main__.Foo'>              ## クラス `Foo`
>>>

クラスメソッドは、主に代替コンストラクタを定義するためのツールとして使用されます。

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## クラスが引数として渡される方法に注意
        tm = time.localtime()
        ## そして新しいインスタンスを作成するために使用される
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

クラスメソッドは、継承などの機能に関するいくつかの難しい問題を解決します。

class Date:
  ...
    @classmethod
    def today(cls):
        ## 正しいクラス(たとえば `NewDate`)を取得する
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
  ...

d = NewDate.today()

演習 7.11:実践におけるクラスメソッド

あなたの report.pyportfolio.py ファイルでは、Portfolio オブジェクトの作成が少し混乱しています。たとえば、report.py プログラムにはこのようなコードがあります:

def read_portfolio(filename, **opts):
    '''
    株式ポートフォリオファイルを、name、shares、および price というキーを持つ辞書のリストに読み込みます。
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

そして、portfolio.py ファイルはこのような奇妙な初期化子を持つ Portfolio() を定義しています:

class Portfolio:
    def __init__(self, holdings):
        self._holdings = holdings
  ...

正直なところ、責任の所在が少し混乱しています。なぜならコードが散らばっているからです。もし Portfolio クラスが Stock インスタンスのリストを含むはずなら、多分クラスをもう少し明確にする必要があります。このように:

## portfolio.py

import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)
  ...

もし CSV ファイルからポートフォリオを読み込みたいなら、多分そのためのクラスメソッドを作るべきです:

## portfolio.py

import fileparse
import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)

    @classmethod
    def from_csv(cls, lines, **opts):
        self = cls()
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

        for d in portdicts:
            self.append(stock.Stock(**d))

        return self

この新しい Portfolio クラスを使うには、今ではこのようなコードを書けます:

>>> from portfolio import Portfolio
>>> with open('portfolio.csv') as lines:
...     port = Portfolio.from_csv(lines)
...
>>>

Portfolio クラスにこれらの変更を加え、report.py コードを修正してクラスメソッドを使うようにしましょう。

✨ 解答を確認して練習

まとめ

おめでとうございます!あなたはデコレートされたメソッドの実験を完了しました。あなたの技術を向上させるために、LabEx でさらに多くの実験を行って練習してください。