質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1934閲覧

別々の関数でそれぞれ使用しているローカル変数のidが一致する理由・意味合いを知りたいです。

Nishizawa1

総合スコア3

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

0クリップ

投稿2020/05/03 10:45

前提・実現したいこと

Python3の勉強中で、仕様について納得のいかない点がありましたので、質問させていただきます。

二つの関数内でそれぞれ適当なリストの変数を定義し、idを取得すると、二つの関数で別々で定義しているにも関わらず、idが一致します。

変数名も、値も異なるにも関わらずidが一致する理由がわからないのですが、
何か意味のある仕様なのでしょうか?
もしくは、何か根本的な事項を見落としているのでしょうか?

ご教授願います。

該当のソースコード

Python

1def test3(): 2 c_list = [3] 3 return id(c_list) 4 5def test4(): 6 d_list = [4] 7 return id(d_list) 8 9print(test3() == test4())

実行結果

True

試したこと、質問した背景

関数のデフォルト引数について勉強をしている際に疑問を持ちました。
下記のコードを実行した際、test2(True)とtest2(False)の実行結果が一致するかどうか調べていました。
(結果はFlaseでした。解釈としては、「test2(True)は関数内で定義されたローカル変数が返ってくる。一方で、test2(False)は関数呼び出しの際に一度だけ定義されるデフォルトの変数がかえってくる。」からと考えています。)

Python

1def test1(a_boolean): 2 if a_boolean == True: 3 a_list = [1] 4 return id(a_list) 5 else: 6 return id(a_list) 7 8def test2(b_boolean,b_list=[]): 9 if b_boolean == True: 10 b_list = [2] 11 return id(b_list) 12 else: 13 return id(b_list) 14print(test1(True) == test2(True)) 15print(test2(True) == test2(False))
True False

補足情報(FW/ツールのバージョンなど)

OS:Windows10
OSバージョン:1909
OSビルド:18363.778
Ubuntu: 18.04.2 LTS (Bionic Beaver)
Python: 3.7.6
PythonはUbuntuに入れたMinicondaからインストールしました。

LouiS0616👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

前提: この手の挙動は実装により異なることがあるので、
CPython の実装 (通常の公式のPython) として話を進めます。

組み込み関数 idより

CPython implementation detail: This is the address of the object in memory.

オブジェクトの "識別値" を返します。
この値は整数で、このオブジェクトの** 有効期間中は **
一意かつ定数であることが保証されています。
** 有効期間が重ならない 2 つのオブジェクトは同じ id() 値を持つかもしれません。**

idが返す値は、メモリ上のアドレスで、同じ値という事は
メモリ上の同じ位置にそのオブジェクトのデータが置かれてるという事を意味します。

b_list, c_list はともにローカル変数で、
関数が終わった後は、この領域はGCにより破棄されます。
つまり、このid() を返すのは無効なアドレスを返していることになります。


最後まで読んでませんでした、デフォルト引数について追記

def test2(b_boolean,b_list=[]): if b_boolean == True: b_list = [2] return id(b_list) else: return id(b_list) # デフォルト引数のid確認 print(id(test2.__defaults__[0]) == test2(False))

デフォルト引数の値は、関数定義時に
test2.__defaults__ のタプルに実体が格納されます。

このアドレスの有効期限は、関数test2が存在する間なので、常に同じ値になります。

投稿2020/05/03 11:28

編集2020/05/03 11:43
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nishizawa1

2020/05/03 11:58

ご回答ありがとうございます。 デフォルト引数の内容にまで言及していただきありがとうございます。 __defaults__に保存されているんですね。 デフォルト引数は関数を一度呼び出せば、保存される変数と認識していたので、 関数の外から呼び出せるだろうなと思っていたのですが、方法がわかりませんでした。 ありがとうございます。 追加で、もしよろしければご回答いただきたいのですが、Cpythonかどうかはどこで確認すればよいでしょうか? (バージョンの詳細を確認すると、GCC 7.3.0と出力されました。GCCは「C言語やC++、Objective-C、Fortran、Java、Ada、Goのコンパイラが同梱されている」とググると出てきたので、これらのいずれかでコンパイルはされていると思うのですが)
teamikl

2020/05/03 12:17

使われたコンパイラとはまた別ですね。 CPython は通称で、(Cythonとも違います) 公式のC言語で書かれたPython処理系を指します。 公式レポジトリの名称 https://github.com/python/cpython CPythonかどうかは、platformというモジュールで調べられます。 https://docs.python.org/ja/3/library/platform.html >>> import platform >>> platform.python_implementation() 'CPython'
Nishizawa1

2020/05/03 15:17

ご回答ありがとうございます。 >>> platform.python_implementation() 'CPython' でした! ただ、あまり勉強できていない範囲のことのようです… python_implementation()とpython_compiler()の意味とか、コンパイル・実装の意味など勉強したいと思います。 ありがとうございました。
guest

0

ベストアンサー

idが同じだから同じオブジェクト、とは言えません。

オブジェクトの "識別値" を返します。この値は整数で、このオブジェクトの有効期間中は一意かつ定数であることが保証されています。有効期間が重ならない 2 つのオブジェクトは同じ id() 値を持つかもしれません。

CPython implementation detail: This is the address of the object in memory.
組み込み関数 — Python 3.8.3rc1 ドキュメント

この辺は実装の中身が絡んでくるので、以下の説明はあくまでも抽象的な比喩くらいに思って読んでもらいたいのですが、ある程度は「オブジェクトを置くのに使うメモリ領域」みたいなものがあらかじめ確保されていて、新しいオブジェクトを作るとその領域の端からオブジェクトの領域に割かれていきます。

c_listtest3の中の処理が終わった瞬間にどこからも参照されなくなって、GC(参照カウンタですが)に回収されていきます。その後、ついさっきc_listが置いてあったところにd_listが作られて、同じidになります。

だから、以下のようにするだけでも結果はTrueではなくなります。

python

1def test3(): 2 c_list = [3] 3 return c_list 4 5def test4(): 6 d_list = [4] 7 return d_list 8 9a = test3() 10b = test4() 11print(id(a) == id(b)) 12

投稿2020/05/03 11:14

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nishizawa1

2020/05/03 11:36

素早いご回答ありがとうございます。 ドキュメントの引用までありがとうございます。 以下の事柄を理解しておりませんでした。 ・あらかじめある程度のメモリ領域が確保されている。 ・上記のメモリ領域が有効期間によって解放とリサイクルが行われる。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問