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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

Q&A

解決済

3回答

2882閲覧

exec (...)は、どうじてこんな事をやるの?

oookabe

総合スコア126

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

0グッド

2クリップ

投稿2018/02/02 05:34

下記のようなPython コードがあるとします。
namespace={....} #辞書
func = compile(codeString, '<string>', "exec")
exec(func, namespace)

不思議な現象:
namespaceは本来コンパイル済みの関数funcに変数値やパラメータを提供するために存在するのに、どうして、execが実行された後、namespaceに沢山の辞書項目が追加されるのでしょうか-----要は元の辞書内容の後ろにPythonのあらゆる固有関数名やデータタイプ名に関する辞書が追加されます。
これらは余計な情報だし、プログラマムの中にたくさんのexecが実行されれば、メモリの浪費ではと思いますね。 
Q1: 理由はなんでしょうか。
Q2: ついでに、元の辞書の後ろに多量の辞書データが追加された場合、
辞書の引く時間に影響はないでしょうか。

宜しくお願いします

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

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

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

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

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

guest

回答3

0

ベストアンサー

コードをだいぶ追っかけましたけど、たぶんフレームの実行時にglobalsへbuiltinsが挿入されるんだと思います。

PyEval_EvalCode
https://github.com/python/cpython/blob/master/Python/ceval.c#L521

PyEval_EvalCodeEx
https://github.com/python/cpython/blob/master/Python/ceval.c#L3940

_PyEval_EvalCodeWithName
https://github.com/python/cpython/blob/master/Python/ceval.c#L3651

globalsに挿入される__builtins__というキー文字列も使い回しの参照ですし、その値である小さくない辞書も使い回しの参照なので、浪費については気にしなくても大丈夫です。C言語ではないのでポインタ数個分のバイト単位のメモリ浪費には目をつぶる度量が必要です。

あと理由については「コードの実行にはグローバル名前空間で__builtins__が参照可能である必要がある」からだと想像します。この点は根拠がなくただの想像ですけどね。

あと最後に辞書をひく時間については、辞書のサイズに関係なく一定です。

投稿2018/02/02 14:53

編集2018/02/02 14:58
YouheiSakurai

総合スコア6142

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

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

oookabe

2018/02/04 13:38 編集

丁寧なご教授本当に有難うございます。 頭が悪くて申し分けございませんが、皆さんの解釈にまだ理解できていません。 私の実験では、例えば、 n1={....} #辞書 func1 = compile(codeString1, '<string>', "exec") exec(func1, n1) n2={....} #辞書 func2 = compile(codeString2, '<string>', "exec") exec(func2, n2) : : nk={....} #辞書 funck = compile(codeStringk, '<string>', "exec") exec(funck, nk) があれば、n1 からnk までにPythonのありとあらゆるbuilt-in関数やデータ型情報や定義が挿入されました(それぞれ6.6KB程度だから、結構な量になります)。メモリ量に気にしなくても、この挿入操作に時間かかるのではと思いますね。  それから何と言っても、Pythonのbuilt-in関数やデータ型情報はPython固有情報で、Python自身がそれを知っているはずで、毎回毎回あっちこっちでユーザー定義の変数(set)に勝手に複製(挿入)するのは何か馬鹿馬鹿しい感じですね(^^)。  理屈を知りたい。。。。。
YouheiSakurai

2018/02/04 14:17

n1, n2 = {}, {} codeString1 = '1' codeString2 = '2' code1 = compile(codeString1, '<string>', 'exec') code2 = compile(codeString2, '<string>', 'exec') exec(code1, n1) exec(code2, n2) print(n1['__builtins__'] is n2['__builtins__']) print(id(n1['__builtins__'])) 一つ目のprintでTrueと表示されるのは、n1に挿入された__builtins__とn2に挿入されたそれが同じである、つまり6.6KB x2ではなく、すでにPythonが作成済みの6.6KBを使いまわしているだけで、思ってらっしゃるような無駄遣いはされていないという事を示します。2つ目のprintで表示される数字はその__builtins__がどこに確保されているかというアドレス情報です。興味があればctypesを使用してそこにどんな情報が格納されているかを読み出すこともできます。理屈は私にも分かりませんが、必ず何か合理的な理由があるはずだと私は考えています。一度Pythonのソースを追ってみてください。で、もっと良い方法が見つかればPythonのメーリングリストで提案してみてください。それしか感じた馬鹿馬鹿しさを肯定する術は無いと思います。
oookabe

2018/02/05 12:13

丁寧なご教授のお陰で、今やっと分かりました!
YouheiSakurai

2018/02/05 12:41

それは良かったです。こちらこそ良い疑問点を上げていただいたので勉強になりました。
guest

0

Python3のリファレンスに、以下のようにあります。

globals 辞書がキー __builtins__ に対する値を含まなければ、そのキーに対して、組み込みモジュール builtins の辞書への参照が挿入されます。ですから、実行されるコードを exec() に渡す前に、 globals に自作の __builtins__ 辞書を挿入することで、コードがどの組み込みを利用できるか制御できます。

あくまですでにあるものを「参照」しているだけなので、「メモリの浪費」にはほぼならないです。

投稿2018/02/02 05:43

maisumakun

総合スコア145123

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

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

oookabe

2018/02/02 10:07

お返答有難うございます。 毎回globals(辞書パラメータ)に挿入データ(text)量は6.6k Bytesくらいで、その分実行時間かかるでしょう。そもそもそういう情報はPython固有の情報で、なぜ毎回毎回exec関数で辞書にパラメータに複製・挿入する必要でしょうか。その意義を知りたいのです。exec関数が10箇所あれば、自分のコードを実行しなくても、66k Bytes の複製・挿入時間かかりますよね。
maisumakun

2018/02/02 12:07

あくまで「キーに参照を挿入している」だけなので、実際に必要なデータは1回の実行につき参照1つ分だけです。
oookabe

2018/02/05 12:12

頭が鈍くて、今やっとご指摘の事:「あくまで「キーに参照を挿入している」だけなので、実際に必要なデータは1回の実行につき参照1つ分だけです。」 分かりました!
guest

0

本当は「自己解決」でなく、皆様親切指導の結果として、このスペースを借りて、自分の心得をメモしておきます。
C言語とよく付き合ってきた人間として、
本来データ格納アドレス(ポインターや参照)に関する概念がしっかりしているはずだが、
やはり、新しい言語の世界に入ったら、迷う現象が起きました。

1.Pythonの「辞書」というやつは、連続しているメモリ域で「value」を保存するのではなくて、
key名別で分配しているアドレスにそれぞれに対応する「value」を保存する。

  1. Pythonのprint文でPythonの「辞書」を丸ごとprintする場合、

  個々key名別に分配しているアドレスに格納している「value」の中身をdumpする。

  1. Pythonの「辞書」というデータ・タイプは個々valueを保存するわけでなく、

  個々valueに対応する保存場所のアドレス(pointer)を保存するだけ。

  1. (これは個人推測ですが)Pythonのprint関数は__builtins__で保存している「value」(Python固有情報)  に対して、解釈を行い、人間が読めるtext文書(built-in 関数とデータ・タイプの説明書)にして、

  文字数が6.6Kにも登る。ただし、この情報の保存本体はexec()に提供するユーザー定義変数内ではない!   そのかわりに builtins に対応するアドレスをexec()に提供するユーザー定義変数の中に挿入される。

有難うございました!

投稿2018/02/05 12:39

編集2018/02/05 12:53
oookabe

総合スコア126

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問