はじめに
このセクションでは、クラス宣言と新しいオブジェクトを作成するアイデアについて説明します。
このセクションでは、クラス宣言と新しいオブジェクトを作成するアイデアについて説明します。
コードを「オブジェクト」のコレクションとして組織化するプログラミング技術。
「オブジェクト」は以下から構成されます。
このコースでは既にいくつかの 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)
>>>
a と b は Player のインスタンスです。
強調:クラス宣言はただの定義です (それ自体では何もしません)。関数定義と同様です。
各インスタンスには独自のローカル データがあります。
>>> 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 ディレクトリにある解決策コードから作業してください。コピーしても構いません。
セクション 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 のインスタンスに name、shares、および 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.name と s.price を書きます。
クラスを使うと、オブジェクトに関数を付けることができます。これらはメソッドと呼ばれ、オブジェクト内に格納されたデータに対して操作を行う関数です。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
>>>
辞書のリストから 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
>>>
report.py プログラムの read_portfolio() 関数を変更して、演習 4.3 で示したように、ポートフォリオを Stock インスタンスのリストに読み込みます。その後、report.py と pcost.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 でさらに多くの実験を行って練習してください。