渡邉一郎日記

趣味かつ独学の成人男性がPython の様々な内容を記事にして発信しております

【Python入門🔰】class(クラス)を作って使ってみよう

Pythonを勉強していく中で「class」に対してとっつきにくい印象を持たれている方も多いのではないでしょうか。

実際に私はそう感じていましたし、まだまだ使いこなせるようになったわけではありません。

人によっては『classって使う?』『関数定義:def しか使ったことない』という場合もあるようで。

この記事では、class の基本的な構成からよく使われる用語の意味などについて実行結果を交えながら説明します。

また、後半では class の使い方をざっくりでも掴んでもらえたら、ということを目標に簡単なコードを書いてみました。

用語などで気持ちが折れそうなときは、後半を読むだけでもOKです。

公式 Doc:Python > 3.11.0 Documentation > The Python Tutorial > 9. Classes

class の構成と用語について

前半は、class の基本構成と機能について説明していきます。

機能面を説明する上で様々な用語が出てきますが、それらについてもしっかり説明していきます。

class の基本構成

class Example:
  """This is a docstring."""
  def example_1(self):
    processing statements
  
  var1 = "variable"
  var2 = 123
  
  def example_2(self):
    processing statements
         .
         .
         .
  def example_N(self):
    processing statements

class は上記のように構成されます。

class の名前(上記では Example)を決めて、その class に持たせたい機能を複数の関数定義(def)を使って記述していきます。

class の中には変数(上記では var1, var2)を記述することも可能で、これらを関数に利用する事もできます。

各関数の第1引数に「self」とありますが、これについては後述します。

class のコンストラクタとインスタンス

class Example:
  # コンストラクタ生成
  def __init__(self, year, month, day):
    self.y = year
    self.m = month
    self.d = day


# インスタンス化
ex = Example(2022, 11, 16)

ex.y
# >>> 2022
ex.m
# >>> 11
ex.d
# >>> 16

class の使い方を調べていくと、「 __init__() 」という関数が出てくると思います。

この関数はコンストラクタと呼ばれ、class のインスタンスを生成した際に自動的に呼び出される関数です。

よって、コンストラクタを定義して引数を渡すことで、インスタンス生成時に特定の初期状態を作り出すことが可能になります。

インスタンスを生成するとは、実際に class を呼び出すという意味になります。
class は設計図、インスタンスはその設計図をもとに作った実物、というように例えられたりします。

「class を定義した=設計図を書いた」
インスタンスを生成した=実際に作った」
といった感じです。

更にざっくり言うと、
インスタンス生成やインスタンス化というのは、実際に class を 変数 に代入した
と捉えてもらえればいいかなと思います。(誰かに怒られそうですが)

データ属性とクラス属性

class Example:
  var = 123
  def example(self):
    return "Hello World"


# インスタンス生成
ex = Example()

# データ属性
ex.data = "data"
print(ex.data)
# 出力結果 >>> data
ex.data = 456
print(ex.data)
# 出力結果 >>> 456

# クラス属性
print(ex.var)
# 出力結果 >>> 123
print(ex.example())
# 出力結果 >>> Hello World

属性というのは、ex.〇〇 の 〇〇 のことを指します。

データ属性は、class 内で定義していない属性にデータを代入すると勝手に出現します。
普段、変数にデータを代入すると、その変数が使えるようになるのと同じです。

違いとしては、class の中に変数とデータが蓄積されるということです。
よって、蓄積したデータ属性は「del ex.〇〇」によって削除することも可能ですし、違うデータで上書きすることも可能です。

ただし、データ属性の名前がクラス属性と重複しないように注意しましょう。せっかく定義した変数が台無しになってしまいます。

クラス属性とは、class 内で定義した変数や関数のことを指します。また、クラスオブジェクトに属している関数のことを「メソッド」ともいいます。

ここで、上記コードのメソッド(上記では ex.example() )に注目すると、定義した時に指定していた第1引数:self が渡されていません。

通常であれば例外(エラー)が発生するのですが、メソッドの独特な機能として第1引数にはインスタンスオブジェクト(上記では ex )が渡されます。

よって、「ex.example() == Example.example(ex) 」ということになります。
そのため、class 内の関数定義では第1引数に「self」を渡しているのです。

インスタンス変数とクラス変数

class Example:
  def __init__(self, year, month, day):
    self.ymd = f"{year}.{month}.{day}"
  
  var = "1970.11.17"


# インスタンス生成
ex1 = Example(2000, 11, 17)
ex2 = Example(2022, 11, 17)

# インスタンス変数
print(ex1.ymd)
# 出力結果 >>> 2000.11.17
print(ex2.ymd)
# 出力結果 >>> 2022.11.17

# クラス変数
print(ex1.var)
# 出力結果 >>> 1970.11.17
print(ex2.var)
# 出力結果 >>> 1970.11.17

インスタンス変数とは、コンストラクタ( __init__() )内の変数を指します(:ymd )。
クラス変数とは、class 内で共有される変数を指します(:var )。

インスタンス変数は、生成したインスタンスごとに独立しています。
クラス変数は、どのインスタンスに対しても共有されています。

よって、インスタンスごとに変化させたい内容やデータに関してはインスタンス変数として定義する。どのインスタンスでも一貫させたいデータに関してはクラス変数として定義する必要があります。

ただし、クラス変数も外部から上書きすることができてしまうため、プライベート変数を使うことも考慮しておくと良いかと思います。

memo

プライベート変数

class Example:
  def __init__(self):
    self.name = "Yamada"
    self.age = 20
    self.__lang = "japanese"
  
  def example(self):
    print(self.name, self.age, self.__lang)


# インスタンス生成
ex = Example()

# インスタンス変数を確認
print(ex.name)
# 出力結果 >>> Yamada
print(ex.age)
# 出力結果 >>> 20
print(ex.__lang)
# 出力結果 >>> AttributeError: 'Example' object has no attribute '__lang'

# メソッドを確認
ex.example()
# 出力結果 >>> Yamada 20 japanese

プライベート変数とは、変数の先頭にアンダースコアを2つ付けることで class の外から簡単に上書きされないようにした変数です。

Pythonでは、先頭にアンダースコアを2つ付けた識別子に対してマングリングを行なう機能があります。(マングリングとは、識別子の名前を内部で変更することです。)

具体的には、上記での「__lang」は「_Example__lang」という変数に内部で変更されます。

よって、class を定義する段階では「self.__lang」で class 内の関数などにも使うことができていましたが、インスタンスを生成した後では「ex.__lang」で認識されることはありません。

ただし、「ex._Example__lang」とすれば認識されて上書きも可能になってしまうので、この点については覚えておきましょう。

継承

# 基底クラス
class FirstClass:
  def setting(self, company, language):
    print(f"//SETTING ---------\n\rCompany:{company}\n\rLanguage:{language}")


# 派生クラス
class SecondClass(FirstClass):
  def setting(self, company, language, mode, job):
    super().setting(company, language)
    print(f"FPS/TPS:{mode}\n\rJob:{job}\n\r-------------------")


# インスタンス生成
fir = FirstClass()
sec = SecondClass()


# メソッド実行
fir.setting("Japan", "Japanese")
"""出力結果
//SETTING ---------
Company:Japan
Language:Japanese
"""

sec.setting("Japan", "Japanese", "TPS", "Magician")
"""出力結果
//SETTING ---------
Company:Japan
Language:Japanese
FPS/TPS:TPS
Job:Magician
-------------------
"""

継承という機能を使うことで、別の class で定義したメソッドを他の class でも利用することができます。

継承元のメソッドに対して継承先で違う引数を渡すことで異なる結果を得ることも可能ですし、継承元のメソッドが持つ機能を拡張することも可能です。

継承元のメソッドを呼び出すには、FirstClass.setting(self, arg) という風にそのまま呼び出すか、super()関数を使うことで可能となります。

また、複数の継承元を指定(多重継承)することも可能です。

class を作って使う

class SelfIntroduction:
  # コンストラクタ生成
  def __init__(self):
    self.company = ""
    self.name = ""
    self.hobby = ""
    self.end = "よろしくお願いします!"
  
  # 自己紹介
  def introduce(self):
    print(f"{self.company}から来ました {self.name} です。",
          f"\n\r趣味は {self.hobby} です。",
          f"\n\r{self.end}"
    )
  

# インスタンス生成
ichiro = SelfIntroduction()
mike = SelfIntroduction()
kevin = SelfIntroduction()

# Ichiro Info
ichiro.company = "日本"
ichiro.name = "一郎"
ichiro.hobby = "けん玉"

# Mike Info
mike.company = "アメリカ"
mike.name = "マイク"
mike.hobby = "野球"

# Kevin Info
kevin.company = "イギリス"
kevin.name = "ケビン"
kevin.hobby = "テニス"


# メソッド実行:Ichiro
ichiro.introduce()
"""出力結果
日本から来ました 一郎 です。
趣味は けん玉 です。
よろしくお願いします!
"""

# メソッド実行:Mike
mike.introduce()
"""出力結果
アメリカから来ました マイク です。
趣味は 野球 です。
よろしくお願いします!
"""

# メソッド実行:Kevin
kevin.introduce()
"""出力結果
イギリスから来ました ケビン です。
趣味は テニス です。
よろしくお願いします!
"""

# 最後の言葉を変える
ichiro.end = "以後お見知りおきを。"
ichiro.introduce()
"""出力結果
日本から来ました 一郎 です。
趣味は けん玉 です。
以後お見知りおきを。
"""

自己紹介を行なうための class を作成してみました。

各人でインスタンス化した後に、自分の情報を入れてメソッドを実行するとそれらの情報を反映した自己紹介文が出力されるようになっています。

また、最後の言葉は「よろしくお願いします!」をデフォルトとして設定していますが、他の変数と同様に変更できます。

class はある種のテンプレートとも捉えられるので、こういった使い方も可能となります。

まとめ

この記事では、class の基本的な構成からよく使われる用語の意味などについて実行結果を交えながら説明しました。

また、後半では class の使い方をざっくりでも掴んでもらえたら、ということを目標に簡単なコードを書いてみました。

class に関する説明は人によって様々で、その人の経験によって多少捉え方に違いがあるように感じます。

ですので、この記事でよくわからなかった場合は、他の方の記事を読んでみてください。
自分に似た感覚の人が書いた記事を読むと、より理解が深まると思います。