プロが教えるわが家の防犯対策術!

pythonのグローバル変数についての質問です。
(1)下記のview_select関数のd_frame変数ですが、その関数内ではd_frameの書き換えは行っていないようなのですが、globalの定義はいるのでしょうか?また、panel変数は書き換えを行っているので、この場合、global d_frameの代わりにglobal panelとしてはいけないのでしょうか?

(2)register関数でのin_entry変数は、この関数内で書き換えは行っていないようなのですが、globalの定義はいるのでしょうか?

(3)pythonのグローバル変数の使い方として、関数定義の前に書かれたグローバル変数と、後に書かれたグローバル変数で何か使い方の違いとかあるのでしょうか?


1 import tkinter as tk
2 from tkinter import ttk
3 from tkinter import messagebox
4 import sqlite3
5
6 books_list = []
7 in_entry = [] # 登録情報入力用のEntry
8 sub_win = None
9 s = ('ISBN', 'タイトル', '著者', '出版社', '発売日',
10 '価格(円)', '画像ファイル')

省略

22 # 一覧の書籍を選択すると呼び出される関数
23 def view_select(event):
24 global img
25 global d_frame
26 slct_items = list_view.selection()
27 s = ('ISBN', 'タイトル', '著者', '出版社', '発売日',
28 '価格(円)')
29 for i in range(len(s)):
30 item_view.item(i, values=(s[i],
31 books_list[int(slct_items[0])][i+1]))
32 try:
33 img = \
34 tk.PhotoImage(file=books_list[int(slct_items[0])][7])
35 except tk.TclError:
36 img = ''
37 panel = tk.Label(d_frame, image=img)
38 panel.grid(row=0, column=1)
39
40 # 登録ボタンをクリックすると呼び出される関数
41 def register():
42 global sub_win
43 global in_entry
44 conn = sqlite3.connect('collection.db')
45 cur = conn.cursor()
46 sql = """INSERT INTO books(isbn,
47 title,
48 author,
49 publisher,
50 release,
51 price,
52 image)
53 VALUES(?, ?, ?, ?, ?, ?, ?)"""
54 ele = []
55 for i in range(len(in_entry)):
56 ele.append(in_entry[i].get())
57 cur.execute(sql, ele)
58 conn.commit()
59 conn.close()
60 messagebox.showinfo('メッセージ', '新規登録しました。')
61 sub_win.destroy()
62 disp()

省略

102 # ウインドウの作成
103 root = tk.Tk()
104 root.geometry('400x300')
105 root.title('蔵書管理アプリ')
106 root.resizable(0, 0)
107 root.grid_rowconfigure(0, weight=1)
108 root.grid_rowconfigure(1, weight=1)
109 root.grid_columnconfigure(0, weight=1)

省略

164 # 詳細部分の作成
165 d_frame = tk.Frame(root)
166 d_frame.grid_rowconfigure(0, weight=1)
167 d_frame.grid_columnconfigure(0, weight=1)
168 d_frame.grid_columnconfigure(1, weight=1)
169 column = ('Item', 'Contents')
170 item_view = ttk.Treeview(d_frame, show='tree',
171 columns=column, height = 6)
172 item_view.column('#0', width=0, stretch='no')
173 item_view.column('Item', anchor='w', width=80)
174 item_view.column('Contents', anchor='w', width=140)
175
176 # 詳細の表示
177 slct_items = list_view.selection()
178 print(slct_items)
179 for i in range(len(s)):
180 item_view.insert(parent='', index='end', iid=i,
181 values=(s[i],
182 books_list[int(slct_items[0])][i+1]))
183 item_view.grid(row=0, column=0)
184 try:
185 img = \
186 tk.PhotoImage(file=books_list[int(slct_items[0])][7])
187 except tk.TclError:
188 img = ''
189 panel = tk.Label(d_frame, image=img)
190 panel.grid(row=0, column=1, sticky=tk.NSEW)
191 c_frame.grid(row=0, column=0)
192 d_frame.grid(row=1, column=0,sticky=tk.NSEW)
193
194 root.mainloop()

A 回答 (2件)

> globalの定義は



定義じゃなくって「宣言」ね。

コード全部見ねぇとなんとも言えないんだけど、一つ言えるのは、Pythonってのは割に「スコープ」はデタラメな言語なのね。
フツー、レキシカルスコープ込みの言語だと(つまり、現行使われてる殆どの言語、だ)、変数xが突然使われた場合には、まず、そのスコープの範囲内、そしてそのスコープの外、そこに無かったらまたその外側、の順番で探しに行くわけ。
言い換えると「グローバル変数」ってのは最後に見つけなアカン変数なんだけど。
一方、Pythonにはそういう「マトモな機構」が実は無い。ハッキリ言っちゃえば設計ミス、って言って良い範疇なんだけど、その「機能の貧弱さ」を フォローする為に、「関数の外」とかで大域変数で定義されてる変数を使う場合はglobal宣言を使わなアカン、って、割にこう、アドホックな解決策になってんのね。
だから、

> 書き換え

書き換えするかしないか、ってのは関係ねぇかな。
単にglobal宣言しとかないと、「だらしない」「設計を失敗してる」Pythonは、その関数内で使われてる変数を「関数内での定義」と思っちゃう、って言うそれだけの話だ。

// JavaScriptの場合

js> let i = 1
1
js> function foo() {
> function bar() {
> function baz() {
> i = 2;
> }
> baz();
> }
> bar();
> }
js> foo()
js> i
2
js>

// 関数fooはローカル関数barを持ってて、その中に関数bazがあって、そこで「関数内でいきなり登場した」変数iに2を代入してる。
// iが何者かわからないので、処理系は「iが何なのか」探しに行く。
// まずは関数baz内のスコープ、次にbar内のスコープ、最後にfoo内のスコープでiを探すが、見つからない。最終的には関数foo外に出てiを発見する。iはグローバル変数だ。
// 結果として、JavaScriptは関数baz内で使われたiはグローバル変数だと判定し、グローバル変数iに2を代入し、実際その通りになる。
// これが「モダンな言語」でのレキシカルスコープの動きで、フツーはこうなる。

# Pythonの場合

i = 1
def foo():
 def bar():
  def baz():
   i = 2
  baz()
 bar()


>>> foo()
>>> i
1

# Pythonにはマトモなレキシカルスコーピングの機能がないので、ローカル関数bazで使われた変数iはグローバル変数iとは別物と判断される。
# 結果、このケースの場合、グローバルで定義したiとローカルで定義されたiは全く別物を判断され、グローバルなiを書き換えたい意図の場合は、アドホックなglobal宣言を行わないとならない。
# ハッキリ言って、この辺はPythonの設計の失敗のウチの一つだと思う。
# しかも「参照は可能」なのでスコーピングの対称性が崩れている。

s = "Hello, World"
def foo():
 def bar():
  def baz():
   print(f"{s}")
  baz()
 bar()

>>> foo()
Hello, World

# 結果、グローバル変数を「参照」する場合は、混乱を防ぐ為、global宣言を付けてた方が良いだろう。
    • good
    • 1
この回答へのお礼

詳しい説明、ありがとうございます。

お礼日時:2022/12/02 22:02

書き換えてなくても 関数外をアクセスするなら


global や nonlocal を付けた方が親切でしょうね。
うっかり初期化してスコープがローカルにジャンプ
したのに気づかず、悩むのをのを防げるし。

ソースにインデント無いので判断が難しいけど
panel は単なる一時変数で良い気がします。

(3)は意味が取りずらいな。
どちらもグローバル変数が作られるという意味では
同じかな。例えば 関数内で
 global a
 a = 1

とすると、関数外に a の宣言が無くてもグローバル変数が
作られます。
    • good
    • 0
この回答へのお礼

ありがとうございます。

お礼日時:2022/12/02 22:02

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!