summaryrefslogtreecommitdiffhomepage
path: root/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html
blob: 09fae4df662e1672a1854c21a8f3713311e15734 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<!DOCTYPE html>
<html lang="ja-JP">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="nsfisis">
    <meta name="copyright" content="&copy; 2021 nsfisis">
    <meta name="description" content="Python における UnboundLocalError の理由と対処法。">
    <meta name="keywords" content="Python,Python 3">
    <meta property="og:type" content="article">
    <meta property="og:title" content="【Python】 クロージャとUnboundLocalError: local variable &apos;x&apos; referenced before assignment|REPL: Rest-Eat-Program Loop">
    <meta property="og:description" content="Python における UnboundLocalError の理由と対処法。">
    <meta property="og:site_name" content="REPL: Rest-Eat-Program Loop">
    <meta property="og:locale" content="ja_JP">
    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
    <title>【Python】 クロージャとUnboundLocalError: local variable &apos;x&apos; referenced before assignment|REPL: Rest-Eat-Program Loop</title>
    <link rel="stylesheet" href="/style.css?h=be0ee160f3d759f939c4a7f090117ae8">
  </head>
  <body class="single">
    <header class="header">
      <div class="site-logo">
        <a href="/">REPL: Rest-Eat-Program Loop</a>
      </div>
      <nav class="nav">
        <ul>
          <li>
            <a href="/about/">About</a>
          </li>
          <li>
            <a href="/posts/">Posts</a>
          </li>
          <li>
            <a href="/slides/">Slides</a>
          </li>
          <li>
            <a href="/tags/">Tags</a>
          </li>
        </ul>
      </nav>
    </header>
    <main class="main">
      <article class="post-single">
        <header class="post-header">
          <h1 class="post-title">【Python】 クロージャとUnboundLocalError: local variable &apos;x&apos; referenced before assignment</h1>
          <ul class="post-tags">
            <li class="tag">
              <a href="/tags/python/">Python</a>
            </li>
            <li class="tag">
              <a href="/tags/python3/">Python 3</a>
            </li>
          </ul>
        </header>
        <div class="post-content">
          <section id="changelog">
            <h2><a href="#changelog">更新履歴</a></h2>
            <ol>
              <li class="revision">
                <time datetime="2021-10-02">2021-10-02</time>: Qiita から移植
              </li>
            </ol>
          </section>
          <div class="admonition">
            <div class="admonition-label">
              NOTE
            </div>
            <div class="admonition-content">
              <p>
                この記事は Qiita から移植してきたものです。 元 URL: <a href="https://qiita.com/nsfisis/items/5d733703afcb35bbf399" rel="noreferrer" target="_blank">https://qiita.com/nsfisis/items/5d733703afcb35bbf399</a>
              </p>
            </div>
          </div>
          <p>
            本記事は Python 3.7.6 の動作結果を元にして書かれている。
          </p>
          <p>
            Python でクロージャを作ろうと、次のようなコードを書いた。
          </p>
          <div class="codeblock">
            <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">def</span><span style="color:#6F42C1"> f</span><span style="color:#24292E">():</span></span>
<span class="line"><span style="color:#24292E">    x </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> g</span><span style="color:#24292E">():</span></span>
<span class="line"><span style="color:#24292E">        x </span><span style="color:#D73A49">+=</span><span style="color:#005CC5"> 1</span></span>
<span class="line"><span style="color:#24292E">    g()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">f()</span></span></code></pre>
          </div>
          <p>
            関数 <code>g</code> から 関数 <code>f</code> のスコープ内で定義された変数 <code>x</code> を参照し、それに 1 を足そうとしている。 これを実行すると <code>x += 1</code> の箇所でエラーが発生する。
          </p>
          <blockquote>
            <p>
              UnboundLocalError: local variable `x’ 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="codeblock">
            <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#6A737D"># 注: var は正しい Python の文法ではない。上記参照のこと</span></span>
<span class="line"><span style="color:#D73A49">def</span><span style="color:#6F42C1"> f</span><span style="color:#24292E">():</span></span>
<span class="line"><span style="color:#24292E">  var x           </span><span style="color:#6A737D">#  f の local変数 'x' を宣言</span></span>
<span class="line"><span style="color:#24292E">  x </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#6A737D">           #  x に 0 を代入</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> g</span><span style="color:#24292E">():        </span><span style="color:#6A737D">#  f の内部関数 g を定義</span></span>
<span class="line"><span style="color:#24292E">      var x       </span><span style="color:#6A737D">#  g の local変数 'x' を宣言</span></span>
<span class="line"><span style="color:#6A737D">      #  たまたま f にも同じ名前の変数があるが、それとは別の変数</span></span>
<span class="line"><span style="color:#24292E">      x </span><span style="color:#D73A49">+=</span><span style="color:#005CC5"> 1</span><span style="color:#6A737D">      #  x に 1 を加算 (x = x + 1 の糖衣構文)</span></span>
<span class="line"><span style="color:#6A737D">      #  加算する前の値を参照しようとするが、まだ代入されていないためエラー</span></span>
<span class="line"><span style="color:#24292E">  g()</span></span></code></pre>
          </div>
          <p>
            当初の意図を表現するには、次のように書けばよい。
          </p>
          <div class="codeblock">
            <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">def</span><span style="color:#6F42C1"> f</span><span style="color:#24292E">():</span></span>
<span class="line"><span style="color:#24292E">    x </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> g</span><span style="color:#24292E">():</span></span>
<span class="line"><span style="color:#D73A49">        nonlocal</span><span style="color:#24292E"> x   </span><span style="color:#6A737D">## (*)</span></span>
<span class="line"><span style="color:#24292E">        x </span><span style="color:#D73A49">+=</span><span style="color:#005CC5"> 1</span></span>
<span class="line"><span style="color:#24292E">    g()</span></span></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">
      &copy; 2021 nsfisis
    </footer>
  </body>
</html>