# 値ってなに?
表現されるもの
すいません、実は自分も未だに、あまりよくわかっていません。 公式マニュアルや、比較演算子の実装をのぞいて見ながら、 すこしずつ理解を深めていきたいと思います。
# 値とはなんだろう?
# ◯ 公式ドキュメントを調べてみる。
実は公式マニュアルでは「値」という言葉が頻繁に使われています。 しかし、実際にその「値」がなんであるのかを説明している箇所はありません。 この値という概念は何者でしょうか?
オブジェクトの値は Python ではやや抽象的な概念です: 例えば、オブジェクトの値にアクセスする正統な方法はありません。 また、その全てのデータ属性から構成されるなどの特定の方法で、 オブジェクトの値を構築する必要性もありません。
比較演算子は、オブジェクトの値とは何かについての特定の概念を実装しています。 この比較の実装によって、間接的にオブジェクトの値を定義している と考えることもできます。
6.10.1. 値の比較 - Python 言語リファレンス (opens new window)
list.remove(x) (opens new window)
リスト中で、値 x を持つ最初の要素を削除します。
該当する項目がなければエラーとなります。
従って immutable であること (immutability) は、 厳密に言えば "値が変更できないこと" と同義ではなく、もう少し複雑です。
So, immutability is not strictly the same as having an unchangeable value, it is more subtle.
3.1. Objects, values and types - Python The Python Language Reference (opens new window)
None
None は、関数にデフォルト引数が渡されなかったときなどに、
値 の非存在を表すのに頻繁に用いられます。
3. 組み込み定数 - Python 標準ライブラリ (opens new window)
引数は 値渡し (call by value) で関数に渡されることになります (ここでの 値 (value) とは常にオブジェクトへの参照(reference) をいい、 オブジェクトの 値 そのものではありません) 。
4.6. 関数を定義する - Python チュートリアル (opens new window)
すべてのオブジェクトは、同一性 (identity)、型、値をもっています。
Python 言語リファレンス (opens new window)
# ◯ 公式ドキュメントの概観
たくさんの公式ドキュメントの文章を引用させていただいたので、 ここで簡単に公式ドキュメントの外観をご説明させていただきます。
上記の文章は「Python チュートリアル」、 「ライブラリーリファレンス」、 「言語リファレンス」から引用しました。 以下の画像は Python の公式ドキュメントのトップページです。
「Python チュートリアル」 は、文字どおり Python を初めて触る人向けて Python の使い方を教えてくれます。
「ライブラリーリファレンス」 は、int, list などの import しなくても使える「組み込み型」、 math, re などの import して使う「標準ライブラリ」の使い方を紹介してくれます。
「Python 言語リファレンス」 は、文法とそれに関連する動作の詳細を教えてくれます。
# ◯ チュートリアルの罠
ただ 「Python チュートリアル」は "プログラミング経験者向け" のチュートリアルなので、自分には結構難しいです。 このチュートリアルという言葉に騙される人を結構ネットで見かけます。
C 言語に心をやられていた自分は Python を触ったときに、 とても感動しました。 こんな初心者向けの言語は、なんて素晴らしんだろうと。
そのため公式チュートリアルを読んだときに、 「せっかく、あれだけ可読性を重視してくれて Guido が文法を作ってくれたのに、 チュートリアルがこんなのじゃ Guido に対する冒涜だ!」 と割と本気思っていました笑
最近知ったのは、このチュートリアル書いたのが、 どうも Guido らしいってことです笑 いま改めて読んでみると、理解はまだ追いついてないのですが、 涙が出るくらい本当に参考になります。
昔は Python をやっている人がほとんどいなかったので、 長いこと、その辺の温度感とかもわからなかりませんでした。 Django のことも 6, 7 年くらい「でぃーじゃんご」って読んでました笑 正しくは「ジャンゴ」と読みます。
# 具体的に触って見る。
# 例 1. int, float クラス
1
と 1.0
というオブジェクトは 1 という値を持っています。
>>> 1 == 1.0
True
>>>
すべてのオブジェクトは、同一性 (identity)、型、値をもっています。
3. データモデル - Python 言語リファレンス (opens new window)
比較演算子は、オブジェクトの値とは何かについての特定の概念を実装しています。 この比較の実装によって、間接的にオブジェクトの値を定義していると考えることもできます。
6.10.1. 値の比較 (opens new window)
例えば、1
の値と
1.0
の値は、等しいです。
>>> 1 == 1.0
True
>>>
でも、1
と 1.0
は、型が異なりますし、
>>> type(1) == type(1.0)
False
>>>
それに、1
と 1.0
は、属性も異なります。
>>> set(dir(1)) == set(dir(1.0))
False
>>>
1
も 1.0
も、
オブジェクトの集まりです。
>>> # 1 オブジェクト
>>> a = 1
>>>
>>> # a の属性を表示
>>> for e in dir(a): e
...
'__abs__'
'... 略 ...'
'__xor__'
'bit_length'
'conjugate'
'denominator'
'from_bytes'
'imag'
'numerator'
'real'
'to_bytes'
>>>
>>> # 1 オブジェクト
>>> a = 1
>>>
>>> # 実部
>>> a.real
1
>>> # 虚部
>>> a.imag
0
>>>
>>> # 1.0 オブジェクト
>>> b = 1.0
>>>
>>> for e in dir(b): e
...
'__abs__'
'... 略 ...'
'__trunc__'
'as_integer_ratio'
'conjugate'
'fromhex'
'hex'
'imag'
'is_integer'
'real'
>>>
>>> # 1.0 オブジェクト
>>> b = 1.0
>>>
>>> # 実部
>>> b.real
1.0
>>> # 虚部
>>> b.imag
0.0
>>>
属性に保持しているオブジェクトは異なりますが、
それぞれのオブジェクトが集まって 1 という値を表現しています。
1
と 1.0
というオブジェクトは 1 という値を持っています。
値 (情報工学) - Wikipedia (opens new window)
値は、何らかの式を評価した結果である。式はデータ型を持ち、評価結果は内部的にはビット列になる。 データ型が異なれば、同じビット列が異なる値(意味)を持つこともある。 例えばあるビット列は整数、浮動小数点数または文字列として解釈されることがある。 (ワイの注釈: Python の 1 と 1.0 の例では、ビット列が異なりますが、 型が異なれば、同じ値として解釈されることもある例を示しました。)
ここで大事なのはオブジェクトそのものが値を持っているということです。 昔の公式のドキュメントでも "object" を "属性" と誤訳していました。 もちろん属性が値を持っていると考えることもできますが、 ここでの訳そのものは間違ったものになってしまっています。
Every object has an identity, a type and a value.
3. Data model - The Python Language Reference (opens new window)
自分も最初、これが object ではなくて attribute だったら訳しやすかったのに、この object が値を持つって何だ... と悩みました。 誰かが間違えたことを明記することは、よくないことですが、それでも、値という概念は、公式サイトを和訳してる人でさえ、 誤訳してしまうくらい、すこし難しい概念ということです。
すべての 属性 は、同一性 (identity)、型、値をもっています。
3. データモデル - Python 言語リファレンス (opens new window)
# 例 2. Person クラス
また、オブジェクトは属性に値を持っていると考えることもできます。 例えば「人」をクラスにすることを考えます。「人」には属性として「名前」、「年齢」、「性別」があります。
変数 person に束縛されたオブジェクトは、"Gaius Iulius Caesar", 30, False という 3 つの値を持っていると言えます。
class Person:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
person = Person("Gaius Iulius Caesar", 30, False)
「オブジェクトそのものが1つの値を持っている。」とも 「オブジェクトが属性に複数の値を持っている。」とも捉えることもできます。
他のオブジェクトに対する参照をもつオブジェクトもあります; これらは コンテナ (container) と呼ばれます。コンテナオブジェクトの例として、 タプル、リスト、および辞書が挙げられます。オブジェクトへの参照自体がコンテナの値の一部です。
ほとんどの場合、コンテナの値というと、コンテナに入っているオブジェクトの値のことを指し、 それらオブジェクトのアイデンティティではありません;
# 例 3. Computer クラス
他にも自分で定義したクラスについて考えてみます。 例えば Computer はCPU, メモリ, SSD と言った部品の集まり、値で構成されています。
メモリはメモリという値として、CPU は CPU という値として、 SSD は SSD という値として認識することができます。
class Computer:
def __init__(self, cpu,
primary_memory, auxiliary_memory):
self.cpu = cpu
self.primary_memory = primary_memory
self.auxiliary_memory = auxiliary_memory
class Cpu:
def __init__(self, clock, core):
self.clock = clock
self.core = core
class Memory:
def __init__(self, volume, clock, type_):
self.volume = volume
self.clock = clock
self.type_ = type_
class Ssd:
def __init__(self, volume):
self.volume = volume
imac = Computer(
Cpu('3.4GHz', 7),
Memory('8GB', '2400MHz', 'DDR4'),
Ssd('1TB'))
# 「オブジェクト」と「値」
# ◯ 「オブジェクト」と「値」の関係
オブジェクトは複数の属性を持っていて、その各属性の中に値が1つはいっています。 オブジェクトと値の関係は、このように再帰的に定義されていると考えることができます。
object -*-> attribute -1-> value(object)
# ◯ 「オブジェクト」と「値」の違い
オブジェクトと値は、おそらく概念的に違います。 オブジェクトは、属性を持っています。値は、属性参照せずに取り扱う概念だと思います。
例えば 1 について、考えて見てください。1
, 1.0
は、四則演算子や比較演算子を通して、
表面的には属性参照することなく扱えるものですし、そもそも属性を意識することさえありません。
他にも 1, "Hello, world!" などのリテラルではなく Computer クラスについても、 我々はパソコンを見る時に Memory, CPU, SSD で構成されたものだとは認識しません。 1つの Computer という値として認識しています。
このように捉えることは、オブジェクト指向で「メッセージ」を意識してプログラミングをする時に、 いくらか役に立つのではないかと考えています。 1, "Hello, world!" と言ったリテラルと同じように属性を意識しないで取り扱えるようになり、 より自然に近い形でコーディングしやすくなるのではないかなと、感じています。
オブジェクト指向は、基本的に「現実世界をモデリングして、 そのモデルのコードを書いたらプログラムができる」ということを目指して作られたものです。 (実際にシステムを作るとモデルと現実世界は必ずしも一緒じゃありませんが)ですので、 オブジェクト指向を考えたお偉いさんが、現実の世界でおこっているある状態をモデル化するときに 「仕事をお願いする」というようなことを「メッセージ」という名前をつけました。
メッセージ脳の作り方 - オブジェクト脳オンライン (opens new window)
もう一度言う。オブジェクトはデータとそれを操作する関数をセットにしたものではない。 オブジェクトはデータエンティティではない。では何か? ... 真のオブジェクト指向の世界では、オブジェクトを生きた有機体のように扱い、 オブジェクトには生まれた日と死ぬ瞬間がある。
また、君が望むなら、アイデンティティや性質を持たせてもいい。 犬にはデータの一部(例えば体重)をくれるよう頼むことができるし、 犬はその情報を返してもよい。 ただ、この犬は能動的なコンポーネントであることを忘れてはいけない。 こちらの要求に対し、何をするかは犬が決めるのだ。
# ◯ 「オブジェクト」が「値」を保存する場所
1つのオブジェクトには、いくつもの値がくっついています。 あるオブジェクトに値をくっつけるには、次の3通りの方法があります。
# 1. オブジェクトの属性 attribute に代入された値 value
object.attribute = value
# 2. シーケンスのインデックス index に割り当てたらた要素 element
squence[index] = element
# 3. マッピングのキー key に割り当てられた項目 item
mapping[key] = item
# 1. オブジェクトの属性
前述の通り。
# 2. シーケンスのインデックス
例えばリストのように s[0] という記述でインスタンスオブジェクトを 参照できるオブジェクトをシーケンスと読んでいます。
s[0] は s の i 番目の要素です。s の属性とは違いそうですが、 なんとなくこれが、値と絡んでいそうな気がします。
s[i] s の 0 から数えて i 番目の要素 4.6.1. 共通のシーケンス演算
>>> # Hello, world! オブジェクト
>>> s = 'Hello, world! '
>>>
>>> # Hello, world! オブジェクトの値
>>> s[0]
'W'
>>>
要素は属性とは違うやろ。って声が聞こえてきそうですが。 シーケンスの要素を使って == を実装したりもできるので、値を構成しうるものとして考えていいのかなと。 ちなみに str には callable でない属性、メソッドでない属性が __doc__ しかありません。
>>> for e in dir(s):
... if not callable(getattr(s, e)):
... e
...
'__doc__'
>>>
# 3. マッピングのキー
例えば辞書 dict のように d['key'] という記述でインスタンスオブジェクトを 参照できるオブジェクトをマッピングと読んでいます。
# ◯ 便利な言葉としての「値」
最初、なぜこのような値という言葉をわざわざ使っているのか理解できませんでした。 正直言っていまもわかっていませんが、確かに 1, "Hello, world!" と言ったリテラルを表現するときには、 値と言った言葉があった方が誰かに説明する時に、わかりやすいのかなと思ってりもしました。
例えば Python を習いたての方にクラスについて説明する時に 「クラスは、インスタンスに共通にする関数とオブジェクトをまとめたものです。」というとわかりにくいのですが、 「クラスは、オブジェクトに共通する関数と値をまとめたものです。」というと、 いい感じに色々とごまかすことができるかなと感じています。
ただ「変数に値を代入する」とか言った方がカッコよかったりして思わず使いたくなってしまうのですが、 抽象的な言葉なので特別な理由がない限りは使わない方が無難かなとも思いました。 覚えた単語は単語は使いたくなりますが...
例えば「変数にオブジェクトを代入する。」を「名前に値を束縛する。」って書かれてたら、 きっと、もう絶対に読みたくなくなるかなと思ったりします笑 自分は、名前は変数と定数を合わせたもの、束縛は代入の上位概念だと理解しています。
# 「リテラル」はどこにあるんだろう?
答え: immutable なオブジェクトがリテラルだと思われます。
再帰的に定義されてるなら、 1 とか 'a' のような実際のデータ、 リテラルは、どこに保存されてるのかな?と思いました。
リテラル (literal) とは、いくつかの組み込み型の定数を表記したものです。
2.4. リテラル - Python 言語リファレンス (opens new window)
何故なら、属性と値の関係は再帰的に定義されてるなら、どんなに属性を参照し続けても実際のデータ, リテラルが保存されているところに行き着くことができないからです。 自己参照してるところを終端として捉えて、 リテラルを保存している場所として取り扱っているのかなと思ったりもします。
例えば int の 1 は自分自身 1 が属性に代入されています。
>>> a = 1
>>>
>>> a.real
1
>>> a.real.real
1
>>>
>>> id(a.real)
4300950496
>>> id(a.real.real)
4300950496
>>>
str の 'W' には自分自身 'W' が 0 番目の要素に代入されています。
>>> s = 'Welcome to ようこそジャパリパーク! 今日もドッタンバッタン大騒ぎ うー!がぉー! 高らかに笑い笑えば フレンズ喧嘩して すっちゃかめっちゃかしても仲良し けものは居ても のけものは居ない本当の愛はここにあるほら 君も手をつないで大冒険 '
>>>
>>> s[0]
'W'
>>> s[0][0]
'W'
>>>
>>> id(s[0])
4473881520
>>> id(s[0][0])
4473881520
>>>
そして、なぜ immutable なオブジェクトが実際のデータを表現していると思ったのかと言うと、 immutable なオブジェクトには __dict__ 属性がないからです。
mutable なオブジェクトは Python は x.a と参照された時、 内部で x.__dict__['a'] に変換して値を参照しています。
>>> class X:
... def __init__(self):
... self.a = 1
...
>>>
>>>
>>> x = X()
>>> x.a
1
>>> x.__dict__['a']
1
>>>
しかし、immutable なオブジェクトには __dict__ 属性が存在しません。 おそらく __dict__ を使わずに属性を固定して(immutable にして)、 リテラルを表現しているものと思われます。
>>> a = 100
>>> a.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__dict__'
>>>
>>> b = range(10)
>>> b.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'range' object has no attribute '__dict__'
>>>
# ◯ str クラスのオブジェクトの構造
Python のテキストデータは str オブジェクト、 すなわち 文字列 として扱われます。 文字列は Unicode コードポイントの イミュータブルなシーケンスです。
4.7. テキストシーケンス型 — Python 標準ライブラリ (opens new window)
文字集合では、個々の文字に対して、 文字集合内での符号位置が決められている。 これをコードポイントという。
文字コード考え方から理解する Unicode と UTF-8 の違い (opens new window)
# おわりに
Python における「値」について概観してきました。 このあとは「比較演算子」を通して、値について考えていきます。
オブジェクトの 値 は Python ではやや抽象的な概念です: 例えば、オブジェクトの値にアクセスする正統な方法はありません。 また、その全てのデータ属性から構成されるなどの特定の方法で、 オブジェクトの値を構築する必要性もありません。
比較演算子は、オブジェクトの値とは何かについての特定の概念を実装しています。 この比較の実装によって、間接的にオブジェクトの値を定義している と考えることもできます。
6.10.1. 値の比較 - Python 言語リファレンス (opens new window)