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

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

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

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

Q&A

解決済

2回答

1932閲覧

【Python】他モジュールの変数をfromつきでimportした場合のアクセスについて

dt9672

総合スコア17

Python 3.x

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

3グッド

3クリップ

投稿2020/04/14 01:21

当初、他モジュールの変数をfromつきでimportし、モジュール名省略で参照および更新することを想定していました。
(グローバル変数的な用途)
しかし実機で試すと以下の通りであり、モジュール名を省略すべきでないように考えられました。

実機で試した結果

Python

1# Main.py 2import Sub 3from Sub import x 4 5print("start") 6print(f" x = {x}") 7print(f" Sub.x = {Sub.x}") 8 9Sub.x = 2 10print("Sub.x -> 2") 11print(f" x = {x}") 12print(f" Sub.x = {Sub.x}") 13 14x = 3 15print("x -> 3") 16print(f" x = {x}") 17print(f" Sub.x = {Sub.x}") 18 19Sub.x = 4 20print("Sub.x -> 4") 21print(f" x = {x}") 22print(f" Sub.x = {Sub.x}")

Python

1# Sub.py 2x = 1

Python

1# Main.pyの実行結果 2start 3 x = 1 4 Sub.x = 1 5Sub.x -> 2 6 x = 1 7 Sub.x = 2 8x -> 3 9 x = 3 10 Sub.x = 2 11Sub.x -> 4 12 x = 3 13 Sub.x = 4

疑問点

質問1.
結果から以下の通り理解しましたが、正しいでしょうか。
0. Subをimportした時点で、Mainにもローカル変数xが定義され、Subのxと同じ値で初期化される
0. "Sub.x"にてSubのxへ、"x"にてMainローカルのxへアクセスするようになる
0. Mainで"x"の値を更新するまでは、Mainにおいて"Sub.x"も"x"も同じ値を持つ
0. Subで"x"の値を更新するまでは、Mainにおいて"Sub.x"も"x"も同じ値を持つ
0. Mainで"Sub.x"を更新すると、Subにおける"x"も同じ値に更新される

質問2.
公式なドキュメントにこの辺りの動作の記載はありますでしょうか。python.orgでは見つけられませんでした。

質問3.
質問1の理解が正しいとすると、他モジュールの変数を使用する際にモジュール名を省略するのはとても危険に感じられました。
一般的にも、他モジュールの変数使用時はモジュール名を省略しないものなのでしょうか。
あるいは他に変数の取り違えを回避するテクニック等あればご教示ください。
(クリーンな設計やコーディング規約の明確化のようなある意味属人的な方法ではなく、システム的な歯止めがあるか知りたいです)

tanat, manipulator, LouiS0616👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

質問 2
https://docs.python.org/ja/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/ja/3/reference/executionmodel.html#binding-of-names

https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements
を総合して把握する必要があるでしょう。

import Subでは以下のことが起こります。

  1. Subというモジュールを探し出してロードする
  2. import文が書かれたところの名前空間に、「Subというモジュール」を「Subという名前」で束縛する

from Sub import xでは以下のことが起こります

  1. Subというモジュールを探し出してロードする
  2. Subがxという属性を持っているかをチェックする
  3. もし持っていない場合、Sub.x というというサブモジュールのインポートを試みる(成功するとSub.xモジュールがSubの名前空間にxという名前で束縛される。失敗したらImportError)
  4. import文が書かれたところの名前空間に、「Sub.x に束縛されているオブジェクト」を「xという名前」で束縛する

質問1.

1 Subをimportした時点で、Mainにもローカル変数xが定義され、Subのxと同じ値で初期化される
"Subをimportした時点" が import Sub のことを言っているならいろいろ間違いです。

  • Subをimportした時点ではモジュールMainの名前空間にSubという変数が定義されてモジュールSubが束縛されます
  • モジュールMainの名前空間に束縛された変数のことはPythonでは通例グローバル変数とは呼ばれます

"Subをimportした時点" が from Sub import x のことを言っているなら、"ローカル変数"や"初期化される"という語の使い方に違和感がありますがだいたい合っています。
(Pythonは「値」を「名前」に束縛するという実行モデルです。その名前のことを便宜上変数と呼びますが、実行モデルに変数という概念はなく、したがって「変数の初期化」という概念もまたありません。一番最初にある名前に値を束縛することを便宜上「変数の初期化」と呼ばなくもない程度かと)

2 "Sub.x"にてSubのxへ、"x"にてMainローカルのxへアクセスするようになる
日本語がおかしいですがだいたいそれで合っています。
"Sub.x"にてSubのxへ、"x"にてMainのグローバル変数xアクセスするようになる
なら合っています。

3 Mainで"x"の値を更新するまでは、Mainにおいて"Sub.x"も"x"も同じ値を持つ
4 Subで"x"の値を更新するまでは、Mainにおいて"Sub.x"も"x"も同じ値を持つ
5 Mainで"Sub.x"を更新すると、Subにおける"x"も同じ値に更新される
ここの「更新する」が曖昧です。
「名前空間でxという名前で別の値を再束縛する」のか「"名前空間でxという名前で束縛されているオブジェクト"が書き換え可能(mutable)な時にその内容を操作する」のかの区別が付いていません。

ここでは Sub.x が整数型で、書き換え不能なオブジェクトなので いずれもは前者の意味だと分かるので、そう解釈すると、どれも合っています。


質問3
質問1の理解が正しいとすると、他モジュールの変数を使用する際にモジュール名を省略するのはとても危険に感じられました。

ちょっと理屈にジャンプがあるように感じます。なぜ危険だと思ったのかが不詳です。

ただ、from 〜 import 〜 を使って他のモジュールから名前束縛をした時、その名前に"別の値を再束縛したりすることは基本しない"し、"書き換え可能なオブジェクトを束縛したりすることも基本しない"かと思います。(緊急回避的なモンキーパッチとして仕方なく、という感覚)

他のモジュールの属性を書き換えたいという事態が起こるなら、そのモジュール設計はそもそもおかしいという感覚です。

投稿2020/04/14 01:52

編集2020/04/14 02:16
quickquip

総合スコア11235

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

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

dt9672

2020/04/14 04:26

動作解説恐れ入ります。 まだ完全に呑み込めていませんが、bind先を丁寧に意識せねばならない (自ずと変数のスコープも把握できる) ことは理解しました。
guest

0

質問1に関しては、概ね正しいでしょう。以下の点について注意してください。

  • Main側の変数xfrom Sub import xの結果です。
  • モジュールのトップレベルの変数は通常グローバル変数と呼ばれます。

質問2に関しては言語リファレンスがあります。

5. インポートシステム — Python 3.8.2 ドキュメント
7. 単純文 (simple statement) — Python 3.8.2 ドキュメント

質問3についてですが、まず「システム的な歯止め」はたぶんないでしょう。ここで想定しているのは、fromで変数自体をimportしようとすると失敗するが、importしてモジュールオブジェクト経由で属性アクセスならできるような変数を作れるか? ということなのですが、そういうものはありません。動的言語ですからもしかしたらできるかもしれませんが、通常そのような実装がなされることはありません。

そもそも他モジュールの変数に直接アクセスする設計はあまり一般的ではないと感じます(fromでimportしようが属性アクセスだろうが)。そのようには使わないか、使うとしても定数的に使うに留めて変更など考えないのが一般的かと思います。
(※一般的な意味の変数についてです。実はクラスも関数も変数に束縛されていると言えるのですが、無視しています。また定数の機能自体もPythonには存在しません。)

モジュールに状態を持たせるなどの目的のためにモジュールの変数を変更したい場合、モジュールのトップレベルでアクセス用の関数を書くという戦略があり得ます。set_x関数のようなものを定義して、global xしたxに対して代入すればセッター風に使えます。モジュールの外に直接xが見えないようにしておきたければ、実際の変数名を_xとしてプライベート変数風にできます。

モジュールは名前空間なのでオブジェクト指向のクラスっぽく扱えなくもないのですが、それをやるならクラスで実装すべき、ということに通常はなるでしょう。また、実装はクラスでやってモジュール内部にインスタンスを隠蔽しておき、それをwrapするためのモジュールに定義した関数を使ってモジュールの属性経由のインターフェースを提供するという実装方針を取ることもあります。標準のrandomはそれです。

投稿2020/04/14 01:45

編集2020/04/14 01:46
hayataka2049

総合スコア30935

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

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

dt9672

2020/04/14 04:26

ご回答ありがとうございます。 べき論としては適切な設計でクリアすべき問題とは思いますので、モジュールの構成見直しやクラス化で対応していきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問