• 締切済み

python: nonlocalとglobal文

pythonで次のような関数の書き方がチュートリアル本に書かれていました ------------------------------------------- スコープと名前空間の例 def scope(): loc = "abc" def do_local(): loc = "local" def do_nonlocal(): nonlocal loc loc = "nonlocal" def do_global(): global loc loc = "global" ------------------------------------------- VS Codeでブレークポイントを置き走らせ、 nonlocal loc → loc = "nonlocal" global loc →loc = "global" の代入(バインディング?)が起こることはわかりました チュートリアル本では、nonlocalとglobalは次のように書かれています 説明文 nonlocal文 その変数が自分を取り囲むスコープにある global文 その変数がグローバルスコープにある 質問; 上の2行(説明文)の 1)nonlocal文 その変数が自分を取り囲むスコープにある 2)global文 その変数がグローバルスコープにある これらのスコープが何、どこ、どのように、指しているか教えてください また 3)これらと名前空間の関係をわかりやすく教えてください (本ではわからず) 【回答上のご注意】 回答は、解答(答え)を求めています わたしはプログラマーではないので、昭和的な「自分で考えろ」的なものは求めていません わからなければ答えない自由もあなたにはあります 不明点があれば説明いたします

  • ketae
  • お礼率85% (292/340)
  • Python
  • 回答数2
  • ありがとう数2

みんなの回答

回答No.2

> まあ資格試験ではそこまで問われていないようなので、これでよしとしようと思います あ〜、資格試験・・・・・・。 模擬試験見た事あるんだけど、「重箱の隅をつつく」ような問題が多くて、あまりプログラミングには役立たないんだよね。 加えると、「国家資格でも何でもない」んで、Pythonの資格試験は私企業的な・・・・・・ま、いっか、その辺は。 > ただ最初の行の loc = "a"を、global文がloc = "global"に書き換えるロジックがまだわからずにいます いや、そこもそのまんまだよ。 loc = "a"は大域的に定義されてるグローバル変数だ。 ローカル関数do_global内でglobal宣言された変数locは「これはdo_global内の変数じゃなくってグローバル変数だ」と言う意味になる。 だからグローバル変数locの中身("a")を「書き換えに行ってる」。 そこはロジックじゃないんだ。単に「宣言したように」振る舞っている。 もう一度言うけど、Pythonのこの挙動・・・っつーかユーザーの手を煩わす「設計方針」ってのは他のモダンなプログラミング言語じゃありえない仕様なんだ。 Pythonはスコープがデタラメだ。よって「ユーザーが書いたようにPythonは解釈する」しかあり得ないんだよ。 Pythonは完全なレキシカルスコープを持っていない。 loc = "a" # ③' do_global() によって "global" に書き換わる def scope(): loc = "abc" # ②' do_nonlocal() によって "non-local" に書き換わる def do_local(): loc = "local" ① ここは外部からアクセス出来ない(見えない) def do_nonlocal(): nonlocal loc ② このスコープの外でlocを見つけたらそれを使う loc = "nonlocal" def do_global(): global loc ③ トップレベル(グローバルスコープ)へとアクセス loc = "global" do_local() # アクセス先はscope()のloc ②' print("A: ", loc, "local代入は変化させない") do_nonlocal() # ここでscope()のloc(②')が書き換わる print("B: ", loc) do_global() print("C: ", loc) # ここの第二引数もscope()のloc(②') scope() print("D: globalで代入したlocのglobalになるはず:" , loc) ここは大域変数③' > ローカル代入がスコープ内でバインディングしない 「バインディング」は「代入」って読み替えてもいい。 バインディング(束縛)ってのは、ちと技術的な用語で、暗黙に「代入とは違いますよ」っつってんだけど、やってる事は(初心者時点では)「代入」と捉えていいです。

ketae
質問者

お礼

遅くなりました この意味をずっと考えていたのですが、global変数の裏での動作(呼び出し)がいまいちわかっていないようです 1回試験受けたのですが、落ちて別の問題をやってだいぶ覚えてきました またよろしくお願いします

回答No.1

まず、Python自体は割にスコープがデタラメな言語で、globalやnonlocalはad-hocな拡張だ、って思っていた方がいい。 要はスコープの設計が「マズい」為、付け足された機能と考えていいんで、あんま美しくないんだ。 まずは「使わなければ使わない方が良い」グローバル変数の話からしよう。(2)の話、からだ。 例えば次のような関数を定義する。 def do_global(): global loc loc = 'global' その後、インタプリタでこの関数を実行する。 >>> do_global() そうすると、インタプリタで入力された筈がないlocと言う変数が「勝手に」定義されてる事が分かるだろう。 >>> loc 'global' 通常、関数内で定義された変数はその外側から覗いたり変更する事が出来ない。関数内での変数は関数内のみで使えてる、と言う「有効範囲」が厳密に決められている(これを「スコープ」と呼ぶわけだ)。 一方、Pythonの関数内でglobal宣言された変数は大域変数、つまり「定義された関数の外側」にアクセスしてる。結果、プログラム内のどの関数からでもアクセスが可能だ。 大域的にアクセス出来るスコープなんでこれをグローバルスコープと呼ぶわけだ。また、言い換えるとグローバルスコープで定義された変数をグローバル変数とか大域変数と表現する。 ただし、モダンなプログラミングではこのグローバル変数を変更したりするテクニックは推奨されない(参照するのは構わないが)。よってこういうプログラミング方法は避けよう。データは引数を通じてやり取りすべきだ。 また、コンピュータサイエンス上、上のdo_global()関数のようにグローバルスコープにアクセスしてそこにある変数を変更したりするものを、通常、関数と呼ばずプロシージャ(手続き)と呼ぶ。 要は関数は作っていいけど、プロシージャは作るな、って事だ。 次は(1)だ。これを理解するには、有名なハッカーであるポール・グレアムの出したアキュムレータの問題を考えるのが一番簡単だろう。 > アキュムレータを生成する関数、すなわち、数nを取り、 「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返すような関数だ。 > > (「増加させる」に注意。ただ足すだけではない。 アキュムレータ(累積器)だから累積させなければ)。 これはPythonだと次のように書けば良さそうなんだが、生憎これじゃ上手く行かない。 def foo(n): def bar(i): n += i return n return bar これをインタプリタ上で次のように実行すると、エラーになる筈だ。 >>> baz = foo(5) >>> baz(1) Traceback (most recent call last): File "<pyshell#116>", line 1, in <module> baz(1) File "<pyshell#114>", line 3, in bar n += i UnboundLocalError: cannot access local variable 'n' where it is not associated with a value 何でこれが出てくるのか、と言うと、ローカル関数としてbarを定義してるが、このbar内で使われてる変数nが「無い」ってぇんで文句を言ってるわけだ。 ちなみに、こういう事はフツーのプログラミング言語では「起きない」。と言うのも、ローカル関数で定義されてるnがそのローカル関数内で見つからなかった場合、その外側に通常は「探しに行く」から、だ(Pythonのスコープがデタラメだ、って言った原因はこれだ)。 例えば同様の関数をPythonのライバルの一つ、JavaScriptで書いても問題なく動作する。 js> function foo(n) { function bar(i) { n += i; return n; }; return bar;} js> baz = foo(5); function bar(i) { n += i; return n; } js> baz(1); 6 js> baz(2); 8 js> baz(3); 11 js> 上のPythonのコードと同様に、ローカル関数bar内に変数nが唐突に登場してるが、JavaScriptの場合、nが何なのか、barが形成してるスコープ外へと探しに行く。 繰り返すが、これが静的スコープ(レキシカル・スコープとも呼ぶ)を持ったモダンな言語では当たり前、なんだけど、Pythonはこの辺へなちょこでそういう機能がない、んだ。 JavaScriptのような動作をPythonで実現するには、コードを次のように書かないとならない。 def foo(n): def bar(i): nonlocal n n += i return n return bar ローカル関数bar内でnonlocalでnを宣言すると、「変数nはこのbarのスコープ内には無い。外へ探しに行きな。」と言う意味になる。これによって、外側のfooが形成してるスコープで、引数になってる変数nを見つけるわけだ。これで無事解決、ってわけ。 >>> baz = foo(5) >>> baz(1) 6 >>> baz(2) 8 >>> baz(3) 11 (3)に付いては、初心者向け説明としてはスコープ≒名前空間、と考えてほぼ間違いない。 もうちょっと専門的に言うと、Pythonは1ファイルで1名前空間を形成するが、その1ファイル内に定義された関数/クラス群はそれぞれに別個で名前空間を生成する・・・よって、1ファイル内で定義された「大域変数」は、単純には別ファイルからそのままアクセス出来る大域変数にはなり得ない(そういう意味では、原義的には「グローバル」じゃない)。 そして、もうちょっと専門的に言うと、「空間」とは数学用語で言うトコの「集合」だ。よってある名前空間内では名前の重複は許されない。単純に、例えば1ファイル内で、 var = 1 def foo(var): return var と書いた場合、大域変数のvarと関数内のvarは一見同じモノに見えるが別物だ。大域変数でのvarはファイル内で唯一無二の変数名になるが、関数は別の名前空間を形成するんで、外に置かれたvarと関数foo内のvarは同じモノじゃない・・・っつーか「違うモノだと保証されている」。両者のvarは同じ名前空間に属していないんだ。

ketae
質問者

お礼

ありがとうございます いただいた回答は難しすぎてわからなかったのですが、VS CodeのデバッグコンソールとAIと100回くらいやりとりしてなんとなく動くコードが書けました テキスト本にあった「ローカル代入がスコープ内でバインディングしない」という意味をAIに聞き、次のコードを試してみました #------- x = 0 def example(): x = 1 print("x is initially", x) def example2(): x = 2 print("x is updated to ", x) example2() print(x) example() #-------------- ローカル代入はバインディングを変更しないのであれば、global文は外側のスコープを読みに行っているのだなと仮定(理解)し、次のコードが動いたので一応理解できたようです #スコープと変数 loc = "a" def scope(): loc = "abc" def do_local(): loc = "local" def do_nonlocal(): nonlocal loc loc = "nonlocal" def do_global(): global loc loc = "global" do_local() print("A: ", loc, "local代入は変化させない") do_nonlocal() print("B: ", loc) do_global() print("C: ", loc) scope() print("D: globalで代入したlocのglobalになるはず:" , loc) #-------------- ただ最初の行の loc = "a"を、global文がloc = "global"に書き換えるロジックがまだわからずにいます まあ資格試験ではそこまで問われていないようなので、これでよしとしようと思います

関連するQ&A

  • globalと$GLOBALSの違いは?

    globalと$GLOBALSの違いは、連想配列かそうでないかしかないのでしょうか。 大量に使う場合、どちらを使っても動作には差はでないでしょうか。 <?php $s = "グローバルスコープ"; //グローバルスコープ function scope_test0(){ echo $s; // 何も表示されない(ローカルスコープ) } scope_test0(); /* globalを使う */ $a = "スコープの違い"; function scope_test1(){ global $a; //ここでglobalを宣言、スコープが違う変数を参照可能に echo $a; //スコープの違い }; scope_test1(); /* $GLOBALSを使う */ $b = 3; $c = 4; function scope_test2(){ $GLOBALS["c"] = $GLOBALS["b"] + $GLOBALS["c"]; } scope_test2(); echo $c; //7 ?>

    • ベストアンサー
    • PHP
  • Python3でのメタプログラミングについて

    下記の Ruby スクリプトと同じことを Python で行う場合、どのように実装すればいいでしょうか? <前提> ・メソッドを Klass クラスに実行時に動的に追加する。 ・追加したメソッドの動的削除、変更は必要ない。 ・メソッド定義は、Python のオブジェクトではなく単なる文字列。 ・メソッド内部から Klass クラス内のインスタンス変数にアクセスする。 ・Klass クラスや Klass インスタンスを扱う処理から、動的に追加したメソッドを呼び出す。 ・追加したメソッドはシングルトンメソッドである必要はない。 <目的> Klass クラスのカスタマイズ機能をメソッド定義文として外部に保存しておいて、実行時に呼び出して機能を拡張するような目的です。Klass クラスはめったに変更されないもの、カスタマイズ機能(メソッド定義文)はよく変更されるもの、という切り分けをしたいため、単純に多重継承や MixIn はしたくないです。(上手い方法があればいいのですが…) #!/usr/bin/env ruby # coding: utf-8 # 文字列のメソッド定義 $user_funcs = [' def user_foo() puts "foo" * @x end ', ' def user_bar() puts "bar" * @x end '] # ↑本来は文字列として外部DBに記録されている。 # ---- # # 拡張したいクラス # class Klass # インスタンス変数を持つ def initialize @x = 3 end attr_accessor :x # 文字列で定義されたメソッドを追加 $user_funcs.each do |f| eval f end end # ---- # # 呼出方法 # obj = Klass.new # 追加されたメソッド(user_から始まる名前)を列挙しながら実行する # => どんな定義がされていても、命名規則さえ守っていれば呼び出し側はそのメソッド名を知る必要はない。 obj.methods.select {|m| /^user_/ =~ m }.each do |m| eval "obj.#{m}" end # 追加されたメソッドはシングルトンメソッドではなく、あくまでもインスタンスメソッドである。 obj2 = Klass.new # インスタンス変数の書き換え obj2.x = 4 # インスタンスメソッドの書き換え def obj2.user_bar() puts "hoge" * @x end obj2.methods.select {|m| /^user_/ =~ m }.each do |m| eval "obj2.#{m}" end

  • Javascriptのスコープについて。

    私は何か大きな勘違いをしているでしょうか? for(var i=0; i<10; i++){ for(var i=0; i<10; i++){ alert(i); } } 単純なこのサンプルで、ループは総計100回回るはずだと思うのですが、 外側ループのローカル変数であるiが、なぜか内側ループのローカル変数iと同一視され、 結果10回しか回りません。 どこがおかしいでしょうか。 比較対象として、以下を実行しました。 var i=3; (function(){ var i = 2; alert(i); })(); alert(i); 結果は2, 3となりました。 スコープは機能しています。 前者はfor文だからおかしくなったのでしょうか? ・・・while文で書き下すと、あ、for文の()中で宣言するカウンタ変数というのは、該当for文のスコープの外に出ちゃっているんですね・・・。 つまり、for(var i・・・)と宣言したカウンタ変数のスコープは、for(){}のスコープではなく、その一個外に所属すると。。 こういう解釈でよいのでしょうか?よろしくお願い致します。m(_ _)m var i=0; while(i<10){ var i=0; while(i<10){ alert(i); i++; } i++; }

  • python:関数が複数globalを読むロジック

    pythonの次のコードがあります #------------------------------------ #code default_name = "Ichiro" def kyodai1(name=default_name): return f"Hello {name}," def kyodai2(name=None): if name is None: name = default_name return f"Hello {name}." default_name = "Jiro" print(kyodai1(), kyodai2()) # 出力 # Hello Ichiro, Hello Jiro. #----------------------------------- kyodai2()関数では、 name = default_name とあり、default_nameは最初の行の"Ichiro"を呼ぶかと思ったら、関数の下にある"Jiro"を読みに行っています default_nameはglobal変数として default_name = "Ichiro" があるのに、def kyodai2()関数だけ default_name = "Jiro" を読みに行った理由がわからずにいます このロジックを3つのAIにコードを読ませて説明させたところ、うち1つが 「(kyodai2スコープ内で)default_nameが定義されていません。この場合、default_name = "Ichiro"ではなく、default_name = "Jiro"を参照します」 と答えました 質問: この説明が正しいかどうかもわからないのですが、なぜkyodai2()関数は、最初のIchiroではなくJiroを読みにいくのでしょうか? Tutolialで該当箇所がわからずにいます 【回答上のご注意】 回答は、解答(答え)を求めています わたしはプログラマーではないので、昭和的な「自分で考えろ」的なものは求めていません わからなければ答えない自由もあなたにはあります 不明点があれば説明いたします

  • pythonのリスト内包?に用いるアルファベット

    リスト内包かどうか確信が持てないでいますが、下記はどちらも動作します コード1のrow コード2のi は、何のアルファベットを用いても動作しますが、何と呼ばれるものでしょうか? 代数、変数、その他? チュートリアルでこの名称とふるまいについて説明した該当箇所がみつけられないので、質問いたしました #コード1 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] saburochu = [row[2] for row in matrix] print(saburochu) # [3, 6, 9] #コード2 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] saburochu = [i[2] for i in matrix] print(saburochu) # [3, 6, 9] 【回答上のご注意】 回答は、解答(答え)を求めています わたしはプログラマーではないので、昭和的な「自分で考えろ」的なものは求めていません わからなければ答えない自由もあなたにはあります 不明点があれば説明いたします

  • python: 例外classの書き方

    pythonで独自の例外classの書き方をチュートリアルで見ていて気づいたことがあります 次のような記載をみつけました class wexal(Exception):  pass 質問の都合上次のように書き換えます class abcde(Exception): 質問: 1)独自の例外クラスの書き方は、(Exception)と記載すれば、abcdeのように名称はなんでも指定できると考えてよいでしょうか 2) Guido van Rossum さん著書のチュートリアル本(ISBN-13 ‏ : ‎ 978-4873119359)をみていると、class の記載の後の名称の最初の文字は大文字になっているものが多くあります  上記で abcdeの部分は、Abcdeでなくても作法上問題ないのでしょうか。つまり(Exception)を記述するときは、最初の文字を大文字にする必要は作法上はないのでしょうか 他の例外の処理については、今回は求めていません。独自例外classの書き方についてのみ質問しています。 【回答上のご注意】 回答は、解答(答え)を求めています わたしはプログラマーではないので、昭和的な「自分で考えろ」的なものは求めていません わからなければ答えない自由もあなたにはあります 不明点があれば説明いたします

  • 変数名の付け方について

    例えば<input type="text" name="aaa">というフォームからのPOSTを受け取るとき、 自分はよく $aaa = $_POST['aaa']; のようにするのですが、変数名とフォームの名前を重複させるのは良くないでしょうか。 またPHPに限らずですが、別のスコープで同じ変数名を使い回すのは問題ないでしょうか。 グローバル変数は慎重に考えますが、ローカル変数はついhogeやtestを使い回してしまいます。 初心者なもので分かりにくい文章になったかもしれませんが、ご教授お願いします。

    • 締切済み
    • PHP
  • javascriptのレキシカルスコープについて

    JSのレキシカルスコープがわかりません。 JSだと、一番外側のスコープで var str = "一番外側のスコープ"; とすると それ以降、ユーザー定義関数の中の、いわゆる関数スコープ内でも var str; と関数内で再定義しない限り、str = "一番外側のスコープ";を つかいまわすことになりますよね? 一度、関数内で、値を変更すると親スコープでもその変更が生きたままになると思います。 var str ="一番外側のスコープ"; function test(){ alert(str); str ="値の変更"; } alert(str) // 関数内で値をかえたけど、それが親スコープにも反映されてしまう。 このことがレキシカルスコープでしょうか? ただ、どこかのサイトで var num = 100; function makefunc() { return function() { alert(num); } } function callfunc() { var num = 50; var func = makefunc(); func(); }; callfunc(); でmakefunc()関数を呼び出した際の 関数内のnumという変数の値が100になるというのです。 実際、実行するとその通りなのですが、 var num = 50; という宣言は、callfunc() という関数の関数スコープ(ローカルスコープ) というのは理解しています。 でその中のいわゆる内部関数というのでしょうか? var func = makefunc(); func(); を実行した際の 変数numというスコープが 一番最初に宣言した  var num = 100; という値をさすというのです。(※そのサイトではこれをレキシカルスコープと呼んでいました) いったどういう動作がレキシカルスコープなのでしょうか? 他にも、クロージャともごっちゃになっております。 識者の方ご教授ください。 参考元はここです http://garden-place.jp/tech/javascript/scope-chain.html

  • 小論文について

    小論文ってどのような構成で、書けばいいのでしょうか? 本とか見たけど、難しくてよくわかりません。 わかりやすく教えていただけらありがたいです。 作文とは違くて、自分の意見をはっきり述べそれについて説明する。みたいな感じ? ちなみに、私の信条ってなんですか? わかる方がいましたら教えてください。 お願いいたします。

  • パッケージ内のグローバル変数

    perlでいまいち理解できないことがあります。 パッケージ内にグローバル変数を作った場合、 外部のスクリプトから完全修飾名で参照できますが、 myによるグローバル変数だけ見ることができません。 レキシカルスコープとはいえ、パッケージ内では グローバル変数にしても参照できないようです。 package MyGlobalVal use vars qw($val1); # これは外部から見える our $val2; #これも外部から見える my $val3; # これは見えない この場合、$val3は外部からなんらかの方法でアクセス 可能なんでしょうか? また参照できない理由はmy宣言により、パッケージの 名前空間とは別の場所(Cだとローカル変数はスタック上に 積まれますよね)にあるから、という理解でよいのでしょうか? アドバイスなどいただけると幸いです。

    • ベストアンサー
    • Perl

専門家に質問してみよう