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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
<!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 'x' referenced before assignment - REPL: Rest-Eat-Program Loop</title>
<meta name="description" content="この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/5d733703afcb35bbf399 本記事は Python 3.7.6 の動作結果を元にして書かれている。 Python でクロージャを作ろうと、次のようなコードを書いた">
<meta name="author" content="">
<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="apple-touch-icon" href="https://blog.nsfisis.dev/apple-touch-icon.png">
<link rel="icon" href="https://blog.nsfisis.dev/favicon.ico">
<meta name="generator" content="Hugo 0.88.1" />
<script>
function setTheme() {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
return;
}
const time = new Date();
const prev = localStorage.getItem('date');
const date = String(time.getMonth() + 1) + '.' + String(time.getDate());
const now = time.getTime();
let sunrise;
let sunset;
function setBodyClass() {
if (now > sunrise && now < sunset) return;
document.body.classList.add('dark');
}
if (date !== prev) {
fetch('https://api.ipgeolocation.io/astronomy?apiKey=5ed37d85103e4defa5df4c5298ed5215')
.then((res) => res.json())
.then((data) => {
sunrise = data.sunrise.split(':').map(Number);
sunset = data.sunset.split(':').map(Number);
})
.catch(() => {
sunrise = [7, 0];
sunset = [19, 0];
})
.finally(() => {
sunrise = time.setHours(sunrise[0], sunrise[1], 0);
sunset = time.setHours(sunset[0], sunset[1], 0);
setBodyClass();
localStorage.setItem('sunrise', sunrise);
localStorage.setItem('sunset', sunset);
});
localStorage.setItem('date', date);
} else {
sunrise = Number(localStorage.getItem('sunrise'));
sunset = Number(localStorage.getItem('sunset'));
setBodyClass();
}
}
</script>
</head>
<body class="single">
<script>
setTheme();
</script>
<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 'x' referenced before assignment</h1>
<div class="post-meta">October 2, 2021</div>
</header>
<div class="post-content"><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 ‘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="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変数 'x' を宣言</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変数 'x' を宣言</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>
<footer class="post-footer">
<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>
</footer>
</article></main>
<footer class="footer">
<span>© 2022 <a href="https://blog.nsfisis.dev">REPL: Rest-Eat-Program Loop</a></span>
<span>·</span>
<span>Powered by <a href="https://gohugo.io/" rel="noopener" target="_blank">Hugo️️</a>️</span>
<span>·</span>
<span>Theme️ <a href="https://github.com/nanxiaobei/hugo-paper" rel="noopener" target="_blank">Paper</a></span>
</footer>
<script src="https://blog.nsfisis.dev/highlight.min.js"></script>
<script>
hljs.initHighlightingOnLoad();
</script>
</body>
</html>
|