# クラスってなに?

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

例えば猫は「猫科」の動物で たいてい「にゃー」と鳴きますから、 それを 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 を使えば、 インスタンスに共通の値と処理を定義することができます。

Hello, world!

# 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']  面倒になったらクラスを使う  くらいに自分は考えています。

データサイエンス系の人のつぶやきを見てたら、 あんまりクラス使わないけどね、って呟かれてたので。

# class と dict の使い分け

dict が立ち位置的に微妙で使い所あるのかなという感じです。 無理して使わなくていいのかなと思ったりします。 YAGNIKISS の精神を大切に、ということでしょうか。

# ◯ 最近の流行り

あまりよくわかっていないのですが、クラス定義文を避ける傾向があるような気がします。

昔 static おじさんという人がいました。 みんな気味悪がっていました笑 恐らく正しいことを言っているんだろうけど、 なんかよく分からないみたいな雰囲気だったような気がします。

いまは関数型言語が注目され static おじさんの流れがあるような気がします。 以下は Ariadne という Python のウェブフレームワークを作っている人が書いているブログです。 正直、内容はあんまりわかっていませんが引用します。

しかし、2019 年、私はサービスを class では書いていません。 But I am not writing service with classes in 2019! Schema-First GraphQL: The Road Less Travelled

これは JavaScript のフレームワーク React の話ですが勉強になります。

元々関数型プログラミング由来の React では、 class コンテナのような状態の塊は嫌われる。 class 作った瞬間に state 以外の暗黙の状態を作られることを、 Reactをよく理解した上級者は嫌ってる。 副作用を起こす手続きを限定することでコードの見通しをよくしたい、のが hooks の動機 https://twitter.com/mizchi/status/1121666161681108992

# ◯ 例えば

でも辞書 dict よりもクラス class の方が多機能だし、 多機能な方を使った方が良いのではないでしょうか? 使わない機能はなるべく、使わないようにする、というのが答えかなと、 個人的に思っています。

例えば WSGI では dict が採用されています。 なに言ってるんだ?って感じだと思います。 WSGI については、以下の動画がとてもわかりやすく、大変オススメです。

動画はわかりやすいですが、自分はあまり理解していません。 WSGI は、ウェブアプリケーション、 例えば Django や Flask の中で使用されている仕組みです。

ウェブアプリケーションは極論、リクエストを受けたら、関数が起動しているだけです。 その時の関数が取る引数には、ユーザ定義クラス class ではなく、あえて dict が採用されています。

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

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

PEP 3333: Python Web Server Gateway Interface

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

いずれにせよ、これだけ広く使われるものに対しても class ではなく dict が 採用されているので、ご自身が class 必要だなと感じるまでは、 dict を使った方が良いのではないかなと個人的に思ったり、思わなかったりしています。

# ◯ まとめ

入門系の書籍ではよく、 「オブジェクト指向は再利用性が高まります。」って煽られるので、 class 定義文を使わないといけないのかなと感じてしまうところがあったので書きました。

class 定義文を使わない方が良いという訳ではないのですが、 YAGNIKISS の精神を大切にして、 そんなに無理して class 定義文を使わなくてもいいかなと思います。

# おわりに

あとは、霧島京子先生に教えてもらうといいかもしれません。

Last Updated: 12/13/2019, 12:24:50 PM