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

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

ただいまの
回答率

87.49%

Pythonのdefについて

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 1,116

score 1

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
Pythonのdefについて質問があります。

発生している問題・エラーメッセージ

以下のコードを実行すると
A: init B: nonlocal C: nonlocal D: global
となるのですが、何故A: local B: nonlocal C: global D: globalとならないのかわかりません。
お手数ですがどなたか教えていただけると助かります。

該当のソースコード

def scope():
    loc = "init"
    def do_local():
        loc = "local"
    def do_nonlocal():
        nonlocal loc
        loc = "nonlocal"
    def do_global():
        global loc
        loc = "global"

    do_local()
    print("A:", loc)
    do_nonlocal()
    print("B:", loc)
    do_global()
    print("C:", loc)

scope()
print("D:", loc)

試したこと

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

ここにより詳細な情報を記載してください。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

loc=""            #1
def scope():
    loc = "init"  #2
    def do_local():
        loc = "local"  #3
    def do_nonlocal():
        nonlocal loc   #<<2
        loc = "nonlocal"
    def do_global():
        global loc
        loc = "global"  #<<1

    do_local()
    print("A:", loc)  #<<2
    do_nonlocal()
    print("B:", loc)  #<<2
    do_global()
    print("C:", loc)  #<<2

scope()
print("D:", loc)  #<<1

3つのlocが存在してるわけで、こう書いとけば理解できるんじゃないかと

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

def scope():
    loc = "init"          # scope's loc bind "init"
    def do_local():
        loc = "local"     # do_local's loc bind "local"
    def do_nonlocal():
        nonlocal loc      # loc is scope's loc
        loc = "nonlocal"  # scope's loc bind "nonlocal"
    def do_global():
        global loc        # loc is global's loc
        loc = "global"    # global's loc bind "global"

    do_local()            # do_local's loc bind "local"
    print("A:", loc)      # scope's loc binding "init"
    do_nonlocal()         # scope's loc bind "nonlocal"
    print("B:", loc)      # scope's loc binding "nonlocal"
    do_global()           # global's loc bind "global"
    print("C:", loc)      # scope's loc binding "nonlocal"

scope()
print("D:", loc)          # global's loc binding "global"


補足事項として今回のコードのようにローカルなスコープ内でも global 宣言して global 変数を定義可能です。
(Python では変数への代入が変数の定義です)
気を付けることとして、変数を参照するだけの場合は外側のスコープの変数になる点があります。
気を付けることとして、関数内のスコープで global, nonlocal の宣言がなく、変数とオブジェクトの束縛関係を更新しない場合は外側のスコープの変数になる点があります。

loc = "foo"
def scope():
    print("E:", loc)      # loc is global's loc
    def do_local():
        print("F:", loc)  # loc is global's loc
    do_local()

scope()
# E: foo
# F: foo
lst = [0, 1, 2]
def scope():
    print("G:", lst)      # lst is global's loc
    def do_local():
        lst[1] = 4        # lst is global's loc   lst 変数の束縛関係は更新されない
        print("H:", lst)  # lst is global's loc
    do_local()
    lst[0] = 3            # lst is global's loc   lst 変数の束縛関係は更新されない
    print("I:", lst)      # lst is global's loc

scope()
# G: [0, 1, 2]
# H: [0, 4, 2]
# I: [3, 4, 2]

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

AとCだけ疑問とのことですので、その点を回答します。

  • Aは、loc = "init"のスコープはdef scope()で、loc = "local"のスコープはdef do_local()なので、print("A:", loc)のスコープであるinitになります。
  • Cは、loc = "global"のスコープはdef scope()の外側であるglobalなので、print("C:", loc)のスコープが優先されて、def scope()のスコープでのlocの最新値であるnonlocalになります。

3つのスコープのlocが登場しますので、それを明確に区別したソースにしてみました。これで、どの時点でどの変数を参照や更新しているか、わかるかと思います。

def scope():
    loc_scope = "init"
    def do_local():
        loc_do_local = "local"
    def do_nonlocal():
        nonlocal loc_scope    # do_nonlocal()の外のスコープ(すなわちscope())の変数を参照
        loc_scope = "nonlocal"
    def do_global():
        global loc_global     # globalスコープの変数を参照
        loc_global = "global"

    do_local()
    print("A:", loc_scope)
    do_nonlocal()
    print("B:", loc_scope)
    do_global()
    print("C:", loc_scope)

scope()
print("D:", loc_global)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る