クラスを使って新しいオブジェクトを作成する

Beginner

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

はじめに

このセクションでは、クラス宣言と新しいオブジェクトを作成するアイデアについて説明します。

これは Guided Lab です。学習と実践を支援するためのステップバイステップの指示を提供します。各ステップを完了し、実践的な経験を積むために、指示に注意深く従ってください。過去のデータによると、この 中級 レベルの実験の完了率は 73%です。学習者から 100% の好評価を得ています。

オブジェクト指向 (OO) プログラミング

コードを「オブジェクト」のコレクションとして組織化するプログラミング技術。

「オブジェクト」は以下から構成されます。

  • データ。属性
  • 振る舞い。オブジェクトに適用される関数であるメソッド。

このコースでは既にいくつかの OO を使用してきました。

たとえば、リストを操作する場合。

>>> nums = [1, 2, 3]
>>> nums.append(4)      ## メソッド
>>> nums.insert(1,10)   ## メソッド
>>> nums
[1, 10, 2, 3, 4]        ## データ
>>>

nums はリストの「インスタンス」です。

メソッド (append()insert()) はインスタンス (nums) に付属しています。

class 宣言

新しいオブジェクトを定義するには、class 宣言を使用します。

class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.health = 100

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def damage(self, pts):
        self.health -= pts

要するに、クラスは、いわゆる「インスタンス」に対して様々な操作を行う関数のセットです。

インスタンス

インスタンスは、プログラムで操作する実際の「オブジェクト」です。

クラスを関数として呼び出すことで作成されます。

>>> a = Player(2, 3)
>>> b = Player(10, 20)
>>>

abPlayer のインスタンスです。

強調:クラス宣言はただの定義です (それ自体では何もしません)。関数定義と同様です。

インスタンス データ

各インスタンスには独自のローカル データがあります。

>>> a.x
2
>>> b.x
10

このデータは __init__() によって初期化されます。

class Player:
    def __init__(self, x, y):
        ## `self` に格納される任意の値はインスタンス データです
        self.x = x
        self.y = y
        self.health = 100

格納される属性の総数や型に制限はありません。

インスタンス メソッド

インスタンス メソッドは、オブジェクトのインスタンスに適用される関数です。

class Player:
  ...
    ## `move` はメソッドです
    def move(self, dx, dy):
        self.x += dx
        self.y += dy

オブジェクト自体は常に最初の引数として渡されます。

>>> a.move(1, 2)

## `a` を `self` に一致させます
## `1` を `dx` に一致させます
## `2` を `dy` に一致させます
def move(self, dx, dy):

慣例として、インスタンスは self と呼ばれます。ただし、実際に使用される名前は重要ではありません。オブジェクトは常に最初の引数として渡されます。この引数を self と呼ぶのは、単に Python のプログラミング スタイルにすぎません。

クラスのスコープ

クラスは名前のスコープを定義しません。

class Player:
 ...
    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def left(self, amt):
        move(-amt, 0)       ## いいえ。グローバル関数 `move` を呼び出します
        self.move(-amt, 0)  ## はい。上のメソッド `move` を呼び出します。

インスタンスで操作を行いたい場合は、常に明示的に参照します (たとえば、self)。

このセットの演習から、前のセクションの既存のコードに一連の変更を加え始めます。演習 3.18 の動作するバージョンがあることが重要です。それがない場合は、Solutions/3_18 ディレクトリにある解決策コードから作業してください。コピーしても構いません。

演習 4.1: データ構造としてのオブジェクト

セクション 2 と 3 では、タプルや辞書として表現されたデータを扱いました。たとえば、株式保有は次のようなタプルで表現できます。

s = ('GOOG',100,490.10)

または次のような辞書で表現できます。

s = { 'name'   : 'GOOG',
    'shares' : 100,
     'price'  : 490.10
}

このようなデータを操作する関数も書けます。たとえば:

def cost(s):
    return s['shares'] * s['price']

しかし、プログラムが大きくなるにつれて、もっと組織的な感覚を与えたい場合があります。そこで、データを表現するもう 1 つの方法は、クラスを定義することです。stock.py という名前のファイルを作成し、株式の 1 保有を表す Stock クラスを定義します。Stock のインスタンスに nameshares、および price の属性を持たせます。たとえば:

>>> import stock
>>> a = stock.Stock('GOOG',100,490.10)
>>> a.name
'GOOG'
>>> a.shares
100
>>> a.price
490.1
>>>

さらにいくつかの Stock オブジェクトを作成して操作してみましょう。たとえば:

>>> b = stock.Stock('AAPL', 50, 122.34)
>>> c = stock.Stock('IBM', 75, 91.75)
>>> b.shares * b.price
6117.0
>>> c.shares * c.price
6881.25
>>> stocks = [a, b, c]
>>> stocks
[<stock.Stock object at 0x37d0b0>, <stock.Stock object at 0x37d110>, <stock.Stock object at 0x37d050>]
>>> for s in stocks:
     print(f'{s.name:>10s} {s.shares:>10d} {s.price:>10.2f}')

... 出力を見てください...
>>>

ここで強調したいことは、Stock クラスがオブジェクトのインスタンスを作成するための工場のように機能することです。基本的には、関数として呼び出すと、新しいオブジェクトを作成してくれます。また、各オブジェクトは個別であり、それぞれ独自のデータを持ち、他の作成されたオブジェクトとは分離されていることも強調する必要があります。

クラスによって定義されるオブジェクトは、辞書に似ていますが、やや異なる構文です。たとえば、s['name']s['price'] を書く代わりに、今では s.names.price を書きます。

演習 4.2: メソッドの追加

クラスを使うと、オブジェクトに関数を付けることができます。これらはメソッドと呼ばれ、オブジェクト内に格納されたデータに対して操作を行う関数です。Stock オブジェクトに cost()sell() のメソッドを追加しましょう。動作は次のようになるはずです。

>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s.cost()
49010.0
>>> s.shares
100
>>> s.sell(25)
>>> s.shares
75
>>> s.cost()
36757.5
>>>

演習 4.3: インスタンスのリストを作成する

辞書のリストから Stock インスタンスのリストを作成するために、次の手順を試してみましょう。その後、合計コストを計算します。

>>> import fileparse
>>> with open('portfolio.csv') as lines:
...     portdicts = fileparse.parse_csv(lines, select=['name','shares','price'], types=[str,int,float])
...
>>> portfolio = [ stock.Stock(d['name'], d['shares'], d['price']) for d in portdicts]
>>> portfolio
[<stock.Stock object at 0x10c9e2128>, <stock.Stock object at 0x10c9e2048>, <stock.Stock object at 0x10c9e2080>,
 <stock.Stock object at 0x10c9e25f8>, <stock.Stock object at 0x10c9e2630>, <stock.Stock object at 0x10ca6f748>,
 <stock.Stock object at 0x10ca6f7b8>]
>>> sum([s.cost() for s in portfolio])
44671.15
>>>

演習 4.4: クラスの使用

report.py プログラムの read_portfolio() 関数を変更して、演習 4.3 で示したように、ポートフォリオを Stock インスタンスのリストに読み込みます。その後、report.pypcost.py のすべてのコードを修正して、辞書ではなく Stock インスタンスと動作するようにします。

ヒント:コードを大きく変更する必要はありません。主に辞書アクセスを s['shares'] から s.shares のように変更するだけです。

以前と同じように関数を実行できるはずです。

>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>> import report
>>> report.portfolio_report('portfolio.csv', 'prices.csv')
      Name     Shares      Price     Change
---------- ---------- ---------- ----------
        AA        100       9.22     -22.98
       IBM         50     106.28      15.18
       CAT        150      35.46     -47.98
      MSFT        200      20.89     -30.34
        GE         95      13.48     -26.89
      MSFT         50      20.89     -44.21
       IBM        100     106.28      35.84
>>>

まとめ

おめでとうございます!あなたはクラスの実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに多くの実験を行って練習してください。