どうも。ケニー(tsujikenzo)です。このシリーズでは、2023年3月から始まりました「ノンプロ研Python中級講座1期」について、全6回でお届けします。今日は3回目です。
前回のおさらい
前回は、「Day1 関数と式」をお届けしました。
今回は、「Day2 オブジェクトとクラス」をお届けします。
今日のアジェンダ
- 今週の成果物
- オブジェクト
- クラス
- インスタンス変数
- メソッド
今週の成果物
まず、VS Codeで直接Git管理できるように、ブログを書きました。
今週は、主にPandasを使った、かんたんな分析を行いました。
お題に選んだのは、事業再構築補助金採択結果です。
公開した記事はこちらです。
noteと合わせて、Day2の振り返りをしていきましょう。
オブジェクト
まず、Pythonが実行されるときの処理を見てみましょう。
ソースコードは動かない
みなさんが書いたコード(ソースコードと呼びます)は、実際には動いているわけではありません。
マシン語(機械語)と呼ばれる、PCが読み込みできる言語に変換されて、はじめて実行されます。
ソースコードは、いきなりマシン語に変換されるわけではありません。(そういうプログラミング言語もあります)バイトコードと呼ばれる、中間的な特殊コードにいったん変換されています。
事後コンパイル方式
JavaScriptと同じように、Pythonは、レキシカルスコープを採用していますので、字句解析を行った時点(コードの実行前、コード中で変数を宣言した時点の情報から)でスコープが決定します。
また、Java Silverで学んだように、バイトコード(中間コード)は、Python仮想マシン(JavaでいうJVM)上で実行され、問題があれば、エラーを吐いて返します。
このように、事後コンパイル(もしくは、事後インタープリター)方式と呼びます。
パソコンの3大要素
それでは、Pythonが実行されるときの、パソコン内部の動きを、HDD、メモリ、CPUの3大要素から見てみましょう。
このように、HDD、メモリ、およびCPUは、プログラムの実行において重要な役割を果たし、相互に連携して効率的な処理を実現しています。
OSが仮想メモリを提供している
とはいえ、Pythonが、パソコンの物理メモリ(DRAM)を直接操作しているわけではありません。そんなことしたらパソコンがぶっ壊れますよね。
そこで登場するのが、OS(オペレーティングシステム)です。OSは、アプリがメモリを安全かつ効率的に利用できるように、「仮想メモリ」という概念を提供しています。
仮想メモリは、物理メモリ上のアドレス空間を抽象化し、仮想メモリから物理メモリへのマッピングや、メモリアクセスの管理を行い、プログラムが連続したアドレス空間を使用できるように働いています。
仮想メモリが提供しているのが、後に登場する、id()関数が返す整数値です。OSが物理メモリを抽象化した、仮想メモリのアドレスを返しているんですね。
Pythonのidとはなにか?
id()関数が返す整数値は、OSが提供する仮想メモリアドレスということを理解しました。
「仮想」なので、何でもアリなのです。わたしが石原さとみとお付き合いすることも、仮想ではアリです。
他にも、数字の10とか、文字列のノンプロ研とかは、書き換えられない値(これをイミュータブルオブジェクトと呼びます)として、もしメモリ上に同じ値があるなら、同一のオブジェクトとして扱おうよ、ということも可能です。
x = 10
y = 10
print(x == y) #True 値の等価性
print(x is y) #True オブジェクトの同一性
その、仮想メモリのアドレス(整数値)では、読みづらいし、覚えにくいので、ラベルを付けたものが「変数」です。
ね、変数は箱ではないでしょ。JavaでもJavaScriptでも。
クラス
JavaScriptでは、オブジェクト指向プログラミングはプロトタイプベースで実装されています。
プロトタイプベース
新しいオブジェクトは既存のオブジェクト(プロトタイプ)を複製して作成され、必要に応じてそのオブジェクトをカスタマイズすることができます。
プロトタイプチェーンは、オブジェクトが特定のプロパティやメソッドを持っていない場合、そのオブジェクトのプロトタイプ(親オブジェクト)を辿ってプロパティやメソッドを検索する仕組みです。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
const person = new Person("Kenny");
person.sayHello(); // "Hello, my name is Kenny" を出力
ES6で導入されたクラス構文は、糖衣構文(シンタックスシュガー)として提供されており、プロトタイプベースの継承をよりクラスベースのように書くことができるようになりました。
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
const person = new Person("Kenny");
person.sayHello(); // "Hello, my name is Kenny" を出力
しかし、言語仕様自体は変わらず、オブジェクトからオブジェクトを生成するプロトタイプベースの仕組みが基本となっています。
したがって、JavaScript中級講座(講師の経験があります)で、クラスのことを「鋳型からたい焼きを作る仕組み」とする比喩は、とても禁じられていました。
クラスベース
いっぽう、Pythonは、オブジェクト指向プログラミングにおいて、クラスという概念を使用してオブジェクトの構造と振る舞いを定義するタイプの言語です。
クラスは設計図のようなもので、オブジェクト(インスタンス)が持つ属性(データ)やメソッド(機能)を定義します。クラスを元にオブジェクトを生成するので、まさにたい焼きと言えるでしょう。
class Person:
def__init__(self, name):
self.name = name
defsay_hello(self):
print(f"Hello, my name is {self.name}")
person = Person("Kenny")
person.say_hello() # "Hello, my name is Kenny" を出力
メモリの挙動が違う
クラスベースでは、インスタンスごとにメモリを確保する(プロトタイプベースは、プロトタイプチェーンを辿る)ので、メモリ使用量は、プロトタイプベースより多いです。
しかし、パフォーマンスに与える影響は、それほど大きくないと言えるでしょう。なのになぜ考察するのでしょうね。
インスタンス変数
インスタンス変数とクラス変数の話だけど、もう疲れたので、コンストラクタの話だけにします。
コンストラクタ
コンストラクタとは、インスタンス生成時に実行される特殊な関数のことです。インスタンスを生成する処理とインスタンスの初期化の処理を行うのが一般的です。
初期化とは
変数は、宣言するだけでは、エラーになります。
x = 10
y
print(x + y) # name 'y' is not defined
変数を宣言したあとに、値を格納することを、初期化と言います。Pythonの場合、変数の宣言と初期化は同時に行われることが一般的です。
x = 5
__init__メソッド
Pythonの__init__メソッドは、インスタンス生成後に呼び出されます。
なので、厳密には、「コンストラクタ」ではなく、_init__メソッドは、イニシャライザと呼ばれています。
メソッド
特殊メソッドについては、次回、モジュール編でお話します。
if __name__ == '__main__:
main()
まとめ
以上で、 「Day2 オブジェクトとクラス」 をお届けしました。
Pandasのこと、ぜんぜん触れてない、むしろプログラミング言語の考察しかしてないじゃないですか。
次回は、 「Day3 モジュール」 をお届けします。