クラスってなに?

インスタンスに共通する
値(クラス変数)や
処理(メソッド)の集まり

例えば猫は「猫科」の動物で たいてい「にゃー」と鳴きますから、 それを class 定義文で書けば次のようになります。

# 対話モード >>> に
# コピペで実行できます。
class Cat:
    type = '猫科'
    
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age
    
    def say(self):
        print(self.name, 'にゃー')

tama = Cat('たま', 'メス', 5)
tora = Cat('とら', 'オス', 3)
tama.type
tama.say()
tora.type
tora.say()
>>> tama.type
'猫科'
>>> tama.say()
たま にゃー
>>> tora.type
'猫科'
>>> tora.say()
とら にゃー
>>> 

このようにして class を使えば、 インスタンスに共通の値と処理を定義することができます。

class をいつ使えばいいの?

この節は Effective Python の 「項目22:辞書やタプルで記録管理するよりもヘルパークラスを使う」の劣化版です。

すこし向きを変えて考えてみたいと思います。 class は、いつ使うべきでしょうか? ごくごくデータが単純なときは tuple を使い、 すこし複雑になったら dict を使い、 厳しそうなら class を使えば、いいかなと思います。

以下、tuple, dict, class の 3 step について、ご説明いたします。 ぱっといい例が思いつきませんでしたので、 全て上の Cat クラスを元に説明いたします。

より良い例は 書籍 Effective Python の 「項目22:辞書やタプルで記録管理するよりもヘルパークラスを使う」を、ご参照ください。

Step 1. tuple

例えば、上で書いた Cat クラスも tuple を使えば、次のようになります。 クラス変数、共通の値である '猫科' は、直接、ベタに書き込んでしまいます。

# 対話モード >>> に
# コピペで実行できます。
tama = ('たま', 'メス', 5, '猫科')
tora = ('とら', 'オス', 3, '猫科')

def say(cat):
    print(cat[0], 'にゃー')

tama[3]
say(tama)
tora[3]
say(tora)
>>> tama[3]
'猫科'
>>> say(tama)
たま にゃー
>>> tora[3]
'猫科'
>>> say(tora)
とら にゃー
>>> 

あまりたいしたものでないものに class を使うのは煩雑だったりします。 例えば上で書いた Cat クラスは __init__ メソッドが長いです。 そんな面倒なときは tuple を使います。

Python の tuple は list ととてもよく似ています。 もともとの由来は、このようなデータ型を表すために作られました。 ABC 言語の Compounds 型に起源をもっています。

もし添字表記 cat[0] だと読みづらくて嫌だった場合は tuple をアンパックすることも1つの方法です。

#
# 対話モード >>> に
# コピペで実行できます。
#
tama = ('たま', 'メス', 5, '猫科')

def say(cat):
    name, _, _, _ = cat
    print(name, 'にゃー')

say(tama)
>>> say(tama)
たま にゃー
>>> 

補足 その1 - tuple のアンパックunpack

Python では以下のように イテラブル を右辺において、 左辺の変数にまとめて代入できます。

#
# 対話モード >>> に
# コピペで実行できます。
#
name, gender, age, family = ('たま', 'メス', 5, '猫科')
name
gender
age
family
>>> name
'たま'
>>> gender
'メス'
>>> age
5
>>> family
'猫科'
>>> 
要素1, 要素2, 要素3, ... = イテラブル

補足 その2

決まりでは無いのですが Python では 使わない変数はアンダーバー _ に代入することが多いです。

#
# 対話モード >>> に
# コピペで実行できます。
#

# 10 回 'Hello, world!'
# 使わない変数は _ に
for _ in range(3):
    print('Hello, world!')
>>> for _ in range(3):
...     print('Hello, world!')
... 
Hello, world!
Hello, world!
Hello, world!
>>> 

まとめ

上記、補足1、2をまとめると以下のような書き方ができます。

#
# 対話モード >>> に
# コピペで実行できます。
#
tama = ('たま', 'メス', 5, '猫科')

def say(cat):
    name, _, _, _ = cat
    print(name, 'にゃー')

say(tama)
>>> say(tama)
たま にゃー
>>>

Step 2. dict

tuple で辛くなったら dict を使います。 うーん、この例でも、わかり辛い... すいません。 Effective Python にいい例があります。

# 対話モード >>> に
# コピペで実行できます。

# リテラルとして書いて、辞書を作る。
tama = {
    'name' :'たま', 
    'gender': 'メス', 
    'age': 5,
    'type': '猫科'
}

# インスタンス化する書き方で、辞書を作る。
tora = dict(
    name='とら',
    gender='オス',
    age=3,
    type='猫科'
)

def say(cat):
    print(cat['name'], 'にゃー')

tama['type']
say(tama)
tora['type']
say(tora)
>>> tama['type']
'猫科'
>>> say(tama)
たま にゃー
>>> tora['type']
'猫科'
>>> say(tora)
とら にゃー
>>> 

dict は2つの作り方があります。 上のコードでは  tama はリテラルとして書く方法で辞書を作りました、   tora はインスタンス化する書き方で辞書を作りました。 

 リテラル  なんていう小難しい言葉を使いましたが、 '0', 'abc' などの書いたままのものをリテラルと言ったりします。

リテラル (literal) とは、いくつかの組み込み型の定数を表記したものです。
2.4. リテラル - Python 言語リファレンス

インスタンス化して作る方法はクォート ' を書かなくて済み、 見た目も綺麗になるので、たまに使います。

Step 3. class

tuple や dict で対応できなくなったら、class を使います。

# 対話モード >>> に
# コピペで実行できます。
class Cat:
    type = '猫科'
    
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age
    
    def say(self):
        print(self.name, 'にゃー')

tama = Cat('たま', 'メス', 5)
tora = Cat('とら', 'オス', 3)
tama.type
tama.say()
tora.type
tora.say()
>>> tama.type
'猫科'
>>> tama.say()
たま にゃー
>>> tora.type
'猫科'
>>> tora.say()
とら にゃー
>>> 

「対応できなくなったら」ってなんやねんって感じですが、 タプルの添字表記 lst[0] や辞書の添字表記 dct['a']  面倒になったらクラスを使う  くらいに自分は考えています。

データサイエンス系の人のつぶやきを見てたら、 あんまりクラス使わないけどね、って呟かれてたので、 そんなに無理して使わなくてもいいかなと...

YAGNIKISS の精神を大切に、ということでしょうか。

◯ dict で class と同じことをする

頑張れば class とほぼ同等のことを dict で実装することができます。

組み込み関数に getattr, setattr というものがあります。 これを辞書に書き換えて class と同じことを実装して遊んでました。

以下のコードは一切、見る必要はありません。

# 組み込み関数 getattr, setattr を...
getattr(ユーザ定義クラスのインスタンス, 属性の名前)
setattr(ユーザ定義クラスのインスタンス, 属性の名前, 属性に代入するオブジェクト)
# 辞書を引数に取るように書き換えて遊んでました。
getattr_(辞書, 属性の名前)
setattr_(辞書, 属性の名前, 属性に代入する辞書)
domodomodomo/emulating_oop.py - GitHubGist

なにが言いたいのかというと、 「dict を組み合わせると class と同じことができます」と言うことです。

 実はオブジェクトは辞書を組み合わせて作られています。  これはたとえ話とかではなくて、実際に本当にオブジェクトは辞書を組み合わせて実装されています。 本当にそうであることを、このさき順番を追って見ていきます。

◯ class では無く dict が採用されているもの

dict が立ち位置的に微妙で使い所あるのかなという感じですが、 WSGI の API で採用されています。なに言ってるんだ?って感じだと思います。 自分もあまり理解していないのですが、 ウェブアプリケーション Django や Flask を使われている場合は、 内部で利用されているものです。

environ はなぜ辞書でなければならないのか? サブクラスを使用すると 何が問題なのか?

辞書を必要とする理由は、サーバ間の移植可能性を最大にすることである。

PEP 3333: Python Web Server Gateway Interface

なぜユーザ定義クラス class はダメで辞書 dict なのか、わかっていないのですが、 メソッドを勝手に付け加えられたら困るからかなと思っています。

ここからさきは...

抽象的な話をします。 もし class を使ったオブジェクトのより具体的な「考え方」や「使い方」を知りたいという方は、 霧島京子先生に教えてもらうといいかもしれません。

Python入門編8: クラスを理解しよう - Paiza






この記事は
「クラスとはなにか」ではなく、 「クラスがどんな構造をしているのか」と「クラスがどんな仕組みになっているのか」について、 ご説明させていただいております。

この記事は
次の3つの記事から構成されています。
1. Python のクラスオブジェクトインスタンスオブジェクトって何?
2. Python のクラス変数インスタンス変数って何?
3. Python の関数メソッドの違いって何?

この記事には
表記の揺れがあります。公式マニュアルの中でクラス class, 型 type という言葉が出てきます。 別のものを指しているのかなと思ったのですが、2つとも同じものを指しています。

Python 2 では組み込み型を type 型、ユーザ定義クラスを class クラス と呼び区別していました。 Python 3 では完全に統合されました。 この文章の中でも表記が揺れてしまっています。どうか、ご容赦ください。

>>> # Python 2
>>> C
<class __main__.C at 0x10ad70a78>
>>> int
<type 'int'>
>>> 
>>> # Python 3
>>> C
<class '__main__.C'>
>>> int
<class 'int'>
>>> 


では、どうぞよろしくお願いいたします。

Last Updated: 9/12/2019, 2:02:10 PM