aboutsummaryrefslogtreecommitdiffhomepage
path: root/docs/posts/2021-10-02/python-unbound-local-error/index.html
blob: b14b37f6a70588847372ecc12671522fb2066077 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<!DOCTYPE html>
<html lang="ja-JP">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
    <title>[Python] クロージャとUnboundLocalError: local variable &#39;x&#39; referenced before assignment | REPL: Rest-Eat-Program Loop</title>
    
    <meta name="description" content="Python における UnboundLocalError の理由と対処法。">
    <meta name="author" content="nsfisis">
    
    <link href="https://blog.nsfisis.dev/an-old-hope.min.css" rel="stylesheet">
    <link href="https://blog.nsfisis.dev/style.css" rel="stylesheet">
    <link href="https://blog.nsfisis.dev/custom.css" rel="stylesheet">
    
    <link rel="icon" href="https://blog.nsfisis.dev/favicon.svg">
    <meta name="generator" content="Hugo 0.88.1" />
    
    
  </head>
  <body class="single">
    <header class="header">
      <nav class="nav">
        <p class="logo"><a href="https://blog.nsfisis.dev">REPL: Rest-Eat-Program Loop</a></p>
      </nav>
    </header>
    <main class="main">


<article class="post-single">
  <header class="post-header">
    <h1 class="post-title">[Python] クロージャとUnboundLocalError: local variable &#39;x&#39; referenced before assignment</h1>
    <div class="post-meta">
      Posted on <time>2021-10-02</time>
    </div>
    <ul class="post-tags">
      <li><a href="https://blog.nsfisis.dev/tags/python">python</a></li>
      <li><a href="https://blog.nsfisis.dev/tags/python3">python3</a></li>
    </ul>
  </header>
  <div class="post-content">
    <section>
      <h1>更新履歴</h1>
      <ul>
        <li>2021-10-02: Qiita から移植</li>
      </ul>
    </section>
    <p>この記事は Qiita から移植してきたものです。
元 URL: <a href="https://qiita.com/nsfisis/items/5d733703afcb35bbf399">https://qiita.com/nsfisis/items/5d733703afcb35bbf399</a></p>
<hr>
<p>本記事は Python 3.7.6 の動作結果を元にして書かれている。</p>
<p>Python でクロージャを作ろうと、次のようなコードを書いた。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">f</span>():
    x <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">g</span>():
        x <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
    g()

f()
</code></pre></div><p>関数 <code>g</code> から 関数 <code>f</code> のスコープ内で定義された変数 <code>x</code> を参照し、それに 1 を足そうとしている。
これを実行すると <code>x += 1</code> の箇所でエラーが発生する。</p>
<blockquote>
<p>UnboundLocalError: local variable &lsquo;x&rsquo; referenced before assignment</p>
</blockquote>
<p>local変数 <code>x</code> が代入前に参照された、とある。これは、<code>f</code> の <code>x</code> を参照するのではなく、新しく別の変数を <code>g</code> 内に作ってしまっているため。
前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。<code>var</code> を変数宣言のための構文として擬似的に利用している。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e"># 注: var は正しい Python の文法ではない。上記参照のこと</span>
<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">f</span>():
    var x           <span style="color:#75715e">#  f の local変数 &#39;x&#39; を宣言</span>
    x <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>           <span style="color:#75715e">#  x に 0 を代入</span>
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">g</span>():        <span style="color:#75715e">#  f の内部関数 g を定義</span>
        var x       <span style="color:#75715e">#  g の local変数 &#39;x&#39; を宣言</span>
                    <span style="color:#75715e">#  たまたま f にも同じ名前の変数があるが、それとは別の変数</span>
        x <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>      <span style="color:#75715e">#  x に 1 を加算 (x = x + 1 の糖衣構文)</span>
                    <span style="color:#75715e">#  加算する前の値を参照しようとするが、まだ代入されていないためエラー</span>
    g()
</code></pre></div><p>当初の意図を表現するには、次のように書けばよい。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">f</span>():
    x <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">g</span>():
        <span style="color:#66d9ef">nonlocal</span> x   <span style="color:#75715e">## (*)</span>
        x <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
    g()
</code></pre></div><p><code>(*)</code> のように、<code>nonlocal</code> を追加する。これにより一つ外側のスコープ (<code>g</code> の一つ外側 = <code>f</code>) で定義されている <code>x</code> を探しに行くようになる。</p>

  </div>
</article></main>
<footer class="footer">
  <span>&copy; 2022 <a href="https://blog.nsfisis.dev">REPL: Rest-Eat-Program Loop</a></span>
  <span>&middot;</span>
  <span>Powered by <a href="https://gohugo.io/" rel="noopener" target="_blank">Hugo️️</a>️</span>
</footer>
<script src="https://blog.nsfisis.dev/highlight.min.js"></script>
<script>
  hljs.initHighlightingOnLoad();
</script>
</body>
</html>