Pythonでは、プログラムが扱うデータは全て「オブジェクト」だ。そこで今回は、オブジェクトとは何か、その性質、変数との関係などについて見ていこう。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
* 本稿は2019年7月12日に公開された記事を、Python 3.12.0で動作確認したものです(確認日:2023年11月10日)。
前回は、Pythonのパッケージについて見た。今回は、Pythonの「オブジェクト」について考えてみよう。
Python公式サイトにあるドキュメント「オブジェクト、値、および型」では、Pythonにおけるオブジェクトとは「データを抽象的に表したもの」と表現されている。つまり、Pythonで書いたプログラムが操作したり、処理したりするデータのことを「オブジェクト」という。
具体的にいえば、「整数や文字列、リスト、タプルなどのデータ」がオブジェクトと考えればよいだろう。前述のドキュメントには「データは全て、オブジェクトまたはオブジェクト間の関係」として表現されるとあるが、これはリストやタプルなどのオブジェクトは他のオブジェクト(への参照)を格納することを表している。
本連載でこれまでに見てきたように、Pythonのオブジェクトには整数値、文字列、リスト、タプルなどの「種類」があり、これを「データ型」あるいは単純に「型」と呼ぶ。オブジェクトの型によって、そのオブジェクトがどのような属性を持つか、どのような演算が可能であるかなどが決定する。第23回「モジュールの作り方」の「オブジェクトと属性」でも述べたが、属性とは「オブジェクト.属性」のようにしてアクセスできるもので、メソッドやパッケージに含まれるモジュールなどがこれに該当する。
例えば、文字列という型に属するオブジェクトであれば、第6回「Pythonの文字列の操作」で紹介したようなメソッドを利用できる。また、「+」演算子は、文字列を被演算子とする場合には文字列の結合を行うし、数値を被演算子とする場合には数値の和を求める。このようにオブジェクトの種類によって、行える処理や行われる処理の内容が変わってくる。
同じドキュメントには「オブジェクトの型は……(中略)……オブジェクトが取りうる値を決定します」ともある。例えば、整数型(int型)のオブジェクトは「任意の整数値」がその値となり、リスト(list型)のオブジェクトであれば「任意の型のオブジェクトを、任意の数だけ並べたもの」がその値となる。
今述べたように、オブジェクトは「型」と「値」を持つが、もう一つ重要な要素がある。それがオブジェクトの「同一性」(アイデンティティー)だ。同じく前述のドキュメントには、オブジェクトの「同一性は生成されたあとは変更されません。これはオブジェクトのアドレスのようなものだと考えられるかもしれません」とある。オブジェクトとは、Pythonプログラムの実行時にコンピュータのメモリ上に作成された「何か」であり、そのアドレス(のようなもの)はそのオブジェクトがメモリから削除されるまでは変わらないということだ。オブジェクトの同一性については次回に取り上げる。
重要なのは、メモリ上にある何かは「全てがオブジェクト」だと考えられることだ。例えば、第15回「ローカル関数とラムダ式」の「関数を変数に代入してみる」では、関数を変数に代入している。Pythonでは「関数もオブジェクト」であり、このような扱いが可能なのである。また、第23回「モジュールの作り方」で作成したモジュール(拡張子が「py」のテキストファイル)を、import文で読み込むと、それに対応した「モジュールオブジェクト」が作成される。これらはそれぞれが、Pythonのコードを基にメモリ上に作成されたオブジェクトであり、関数は「function」という型に、モジュールは「module」という型に属する。
本連載のここまでのおさらいがてら、これまでに登場してきたオブジェクトの種類を幾つか以下に一覧しておこう。
型 | 説明 |
---|---|
int | 「整数」を表現する型 |
float | 「浮動小数点数」を表現する型 |
bool | TrueかFalseのいずれかの値を取り、真偽値(条件が成立しているかどうか)を表現する型 |
str | Unicodeのコードポイント値を任意の数だけ並べた「文字列」を表現する型(変更不可能) |
list | 任意の型の要素を任意の数だけ並べた「リスト」を表現する型(変更可能) |
tuple | 任意の型の要素を任意の数だけ並べた「タプル」を表現する型(変更不可能) |
dict | キーと値の組からなる要素を任意の数だけ並べた「辞書」を表現する型(変更可能) |
set/frozenset | 任意の型の要素を任意の数だけ並べた順序のない「集合」を表現する型(setは変更可能。frozensetは変更不可能) |
function | 「関数」を表現する型 |
module | 「モジュール」や「パッケージ」を表現する型 |
本連載でここまでに取り上げたオブジェクトの型(一部) |
この中には共通の特性を持った型が幾つか存在する。例えば、文字列とリストがそうだ。文字列は「Unicodeのコードポイント」値を要素として、それらを任意の数だけ並べたオブジェクトだ。要素には「インデックス」を使用してアクセスできる。これに対して、リストは「任意の型のオブジェクト」を要素として、それらを任意の数だけ並べたオブジェクトだ。要素にはやはり「インデックス」を使用してアクセスできる。タプルも同様だ。これらのオブジェクトには「インデックス指定が可能」という共通する性質がある。
この「インデックス指定が可能」という観点からは、これらは同種のオブジェクトであり、Pythonでは、これらを「順番を持った要素」を格納するオブジェクトとして「シーケンス型」として扱われる(シーケンスとは「順序」とか「一連の」といった意味)。
これまでの連載の中で、リストや文字列、タプルで「特定の要素があるかどうか」を調べるにはin演算子やnot in演算子を使用すると述べてきたが、実はこれらの演算子はシーケンス型に属するオブジェクトに共通して適用できるものだ。あるいは角かっこ「[]」によるインデックス指定やスライスもシーケンス型に属する型で共通の操作である。
一方、リストは変更可能で、文字列とタプルは変更不可能だ。さらに文字列とタプルでは、格納できる要素の種類が異なる*1。こうした差によって、オブジェクトに対して行える操作が異なってくる。と同時に、各型の特徴(用途やそのオブジェクトがどんな値を取れるかなど)が明確になる。
*1 難しい話になるが、これらの型の特性や差異はcollections.abcモジュールのSequence/MutableSequenceなどの抽象基底クラスによって特徴付けられている(Sequenceクラスは変更不可能なシーケンスを抽象的に表す型で、MutableSequenceクラスは変更可能なシーケンスを抽象的に表す型)。このことは「import collections.abc」を実行して、「issubclass(list, collections.abc.Sequence)」「issubclass(tuple, collections.abc.MutableSequence)」などを実行してみると分かる。リストは「シーケンス」の一種なので前者の実行結果はTrueに、タプルは「変更不可能なシーケンス」なので後者の実行結果はFalseになる。
同様なことは数値を扱うためのint型とfloat型にもいえる。前者は整数値を扱うためのもので、後者は浮動小数点数(実数)を扱うためのものであり、コンピュータ内部での数値の表現方法に違いがあるために、これらは別種の型として扱われる。ただし、整数や浮動小数点数が混在した数値演算は問題なく行える*2。
*2 上の脚注と同様に、数値全般を抽象的に表す型としては、numbersモジュールのNumberクラスがある。これを頂点として、整数や浮動小数点数、複素数を表す型などがPythonでは用意されている。
これは余談だが、実はTrue/Falseの真偽値を扱うためのbool型も整数を扱う型の一種である。第9回「if文による条件分岐」の「if文の条件と比較演算子とブール演算子」では「0や0.0はFalseと見なされ、それ以外の数値はTrueと見なされる」という話をしたが、True/Falseを数値として見たとき、Trueは「1」に、Falseは「0」に見なされる。
print(1 + True)
print(100 * False)
実行結果を以下に示す。Trueが「1」として、Falseが「0」として数値との演算が行われていることを確認してほしい。
話を戻すと、「シーケンス型とリスト型の関係」と同様な関係は辞書や集合にもいえる。辞書は「マッピング型」の一種であり、「集合型」には変更可能な集合を表現するset型と変更不可能な集合を表すfrozenset型があるといった具合だ。
このように、共通の特徴を持ちながら、微妙な差異を特徴として持つ型がPythonには多数用意されている。「どんな種類のデータをどのようにして扱いたいか」によって、どの型を使えばよいかはおのずと決まってくるはずだ(Pythonが標準で用意してくれるものでは足りなければ、外部ライブラリを使ったり、独自に型を作成したりすることになる)。
オブジェクトの型を調べる最も簡単な方法は、組み込みの「type関数」を使うことだ。
type(obj)
objに渡したオブジェクトの型を表す型オブジェクトを返す。パラメーターを3つ持つバージョンについては、本稿では説明を省略する。
パラメーター | 説明 |
---|---|
obj | 型を調べたいオブジェクト |
type関数のパラメーター |
使用例を以下に示す。
print(type([1, 2, 3])) # リストの型
print(type(1)) # 整数の型
print(type('string')) # 文字列の型
type関数の戻り値は、「オブジェクトの型を表す」型オブジェクトとなる。実行結果を見ると分かるが、これは一般に「<class '型名'>」のように表現される。
オブジェクトの型を調べる方法としては、この他にisinstance関数もあるが、これについてはクラスの説明をするときに取り上げよう。
Copyright© Digital Advantage Corp. All Rights Reserved.