この記事について
以前書いたこの記事において、「ここがよくわからない」ポイントとして挙げたものを調べて簡単にまとめた。特にクラス、クラスメソッド周りが自分的にわかりにくかったので文章多めです。
今回もPythonの言語仕様関連の内容になります。
クラス
クラスとは
クラスとは、オブジェクトの種類を意味する。クラスはオブジェクト指向プログラミングにおけるオブジェクトを扱うのに重要な役割を持つ。
クラスを定義するとその中でクラスの属性やクラスメソッドを指定できる、属性とはオブジェクトごとに保持できる値。
新しいクラスを定義すると、そのクラスに属するオブジェクトを作ることができるようになる。作成されたオブジェクトの型はそのクラスになる。
クラス定義の記述例
class クラス名:
def __init__(self, n):
self.n = n
def メソッド名(self, 引数, ...):
実行文
def メソッド名(self, 引数, ...):
実行文
...
メソッド定義は関数定義と同じ形をしていますが、クラス定義の中に記述する。メソッド定義において、その最初の引数には慣例として self
という名前を付けます。この引数には、メソッドが呼び出されたオブジェクト自身が渡される。
__init__
は初期化メソッドで、コンストラクタに引数を与えればインスタンス作成後に呼び出される。ここでは、オブジェクトのnという値に引数で指定されたものを代入している。
蛇足
__func__
という名前づけの関数は特殊メソッド(magic method)と呼ばれる。クラス定義の中で特殊メソッドを定義すると、そのクラスのオブジェクトに対して、 その特殊メソッドに対応する機能が付与される。 特定の特殊メソッドを持っているクラスはリテラル型や数値型、あるいはマップ型のように振る舞うことができる。
逆にいうと、特殊メソッドに None
を設定することは、それに対応する演算が利用できないことを意味する。例えば、クラスの __iter__()
を None
に設定した場合、そのクラスはイテラブルにはならず、そのインスタンスに対し iter()
を呼び出すと TypeError
が送出される。
この記述方式はメソッドに限らず利用され、特殊属性とも呼ばれる。
アンダースコアを利用した名前記述規則はPEP8で決められていて、他にも三つほど種類がある。
クラスで定義できるもの
インスタンスごとに値が変わるもの(属性)
- インスタンス変数
→クラス内のメソッド定義の中で定義(self.変数名) - メソッド
クラスで共通のもの
- クラス変数
- クラスメソッド
- スタティックメソッド
クラスメソッドとスタティックメソッド、インスタンスメソッド
インスタンスメソッド(通常のメソッド):必ず第一引数にself(インスタンス)をとる。インスタンスの持つデータを使って処理をする。
クラスメソッド:必ず第一引数にcls(クラス)をとる。クラスで共通した処理をする。呼び出しにインスタンスの生成は不要。
スタティックメソッド:クラスで共通した処理をするが、クラスやインスタンスに一切関わらない処理。呼び出しにインスタンスの生成は不要。
インスタンスメソッドは通常のオブジェクトのメンバ関数と同じ理解で良いと思われる。クラスメソッドはクラスに属する処理、スタティックメソッドはクラスの中に定義するけどクラスやインスタンスとは関連性がない関数と使い分けられば良さそう。
クラスメソッドとインスタンスメソッドの違い
違いはざっくりいうと二つ
- スタティックメソッドはクラスについて何も知らず、引数で渡されたパラメータを処理するだけ
- クラスメソッドはクラスのパラメータの一部として機能する
クラスメソッドは、インスタンスメソッドが暗黙の第一引数としてインスタンスをとるように、第一引数としてクラスをとる。そのためクラスメソッドは C++ や Java のスタティックメソッド(静的メソッド)とは少し違う。
一方で、スタティックメソッドは暗黙の第一引数を受け取らない。クラスにもインスタンスにも一切依存しない処理を行う。
継承
クラスは継承も可能。継承とは、既存のクラスをもとにしてクラスを作ること。継承もとを親、継承先を子と呼ぶ。
継承したクラスは親クラスからの変更部分のみ実装して定義できる。
関数
関数とは
関数は処理(手続きの流れ)をまとめた再利用可能なコード。
関数は、呼び出しもとから渡された値へ変化を与える際にも用いる。また、関数は変数として扱うことも可能。
関数の特徴は以下の三つ。
- 名前を持つ。
- 手続きの流れを含む。
- 返値(明示的あるいは非明示的に)を返す。※
※返値は関数内の retrun
で定義。returnの後ろに記述した値が返されるが、何も記載されていない場合やそもそも関数内でreturnが記述されなかった場合はNoneを返す。
変数のスコープについて
関数の引数や関数内で定義される変数はローカル変数のため、それらの変数は関数の外からは参照不可。一方、グローバル変数は関数の外からも中からも参照可能。
グローバル変数と同じ名前の変数を関数内で定義すると、通常はグローバル変数ではなく関数内のみで利用可能なローカル変数の定義として扱われる。引数も同様となる。
そのため、グローバル変数に関数内で値を代入するには(自動的にローカル変数と扱われるため)明示的にグローバル変数と表す必要がある。
#グローバル変数greeting_globalに値を代入する関数greeting
def greeting():
global greeting_global
greeting_global = 'Bonjour'
print(greeting_global)
global宣言なしに同名のグローバル変数とローカル変数を参照しようとするとエラーか意図しない動作になるため、同名の二種の変数は使うべきではない。
関数とメソッド
関数は単体の処理、メソッドはクラス内で定義される処理。
蛇足
- 可変長引数
引数の数を指定せず宣言することが可能。その引数にはタプルやリストを渡すことが可能(渡す時にはリストやタプルの変数名の前に*
をつける)。
#可変長の引数を受け取り、それらを表示する関数greeting
def greeting(*args):
print(args)
#可変長の引数を受け取る関数greetingに複数の引数を渡して呼び出し
greeting('Hello','Bonjour','Guten Tag')
#リスト型オブジェクトgreeting_listを関数greetingに渡して呼び出し
greeting_list = ['Hello','Bonjour','Guten Tag']
greeting(*greeting_list)
- 辞書型可変長引数
#可変長のキーワード引数を受け取り、それらを表示する関数greeting
def greeting(**kwargs):
print(kwargs)
#可変長のキーワード引数を受け取る関数greetingに複数の引数を渡して呼び出し
greeting(en='Hello', fr='Bonjour', de='Guten Tag')
#辞書型オブジェクトgreeting_dictを関数greetingに渡して呼び出し
greeting_dict = {'en': 'Hello', 'fr': 'Bonjour', 'de': 'Guten Tag'}
greeting(**greeting_dict)
関数で利用されるアンダースコア
関数名に埋め込まれるアンダースコアについてはPEP8のコーディング規約で定義されている。
_function()
関数名の先頭に一つ入れる場合
その関数を内部用(praivate)と明示している。ただし、インタープリタやコンパイラによってそう変換されるわけではなく、普通に hoge._funtion()
で呼び出しはできてしまう。
function_()
関数名の末尾に一つ入れる場合
別の関数や変数と被らせないための回避方法。
予約語と同じ名前の関数を作りたいときに使う。
__function()
関数名の先頭に続けて二つ入れる場合
クラス内に定義することで、擬似的なprivate属性を作れる。これは二つ前の例と違い、コンパイル後に関数名が変わる(ネームマングリング機構)。
変更後は _Classname__function()
という形式になるので、hoge.__function()
では呼び出しできない。
なお、 hoge._Classname__function()
なら呼び出せる。
function()
関数名の前後両方に続けて二つ入れる場合
前述した特殊メソッド。クラスに属するオブジェクトの型の挙動に関わるような機能を実装するのに使われている。
既存のメソッドが多く、オーバーライドをすることはあるが開発作業で新規に実装されることは基本的にされない。
ネストされたスコープ
スコープがややこしい問題。
pythonでは、関数の中に関数を定義することができる。
外側の関数において宣言された変数は、内側の関数で参照することができる。ただし、外側の関数で宣言された変数について、デフォルトでは内部の関数から値を代入することはできない。
参考
https://docs.python.org/ja/3/glossary.html
https://utokyo-ipp.github.io/index.html
→幾つかのソースコードはこちらから引用させていただいています。
https://www.programiz.com/python-programming/methods/built-in/classmethod