はじめに
-
global
文とnonlocal
文はいつ使うの?- 「関数の外側のスコープにある変数」を関数の内側のスコープで 変更 したいとき
予約語 | 変更対象となる変数 |
---|---|
global 文 |
一番外側のスコープにある変数(グローバル変数) |
nonlocal 文 |
nonlocal 文が使われた関数の外側のスコープにある変数 |
1. global 文
- global 文はいつ使うの?
- 関数の中でグローバル変数を変更したいとき
O 正しいコード
#
# 対話モード >>> に
# コピペで実行できます。
#
間隔 = 1
def 間隔を設定する(新しい間隔):
global 間隔
print(間隔)
間隔 = 新しい間隔
間隔を設定する(2) # 1
間隔 # 2
X 誤ったコード
間隔 = 1
def 間隔を設定する(新しい間隔):
# 1. global 文を使わずに
# global 間隔
# 2. グローバル変数を参照してから
print(間隔)
# 3. ローカル変数を定義しようとすると
間隔 = 新しい間隔
# 4. 例外が発生します
間隔を設定する(2)
# UnboundLocalError:
# cannot access local variable '間隔'
# where it is not associated with a value
間隔 # 1
長い解説
間隔 = 1
def 間隔を設定する(新しい間隔):
# 1. global 文を使わずに
# global 間隔
# 2. 関数の外側の変数を参照してから
print(間隔)
# 3. 関数の外側の変数と同じ名前を変数を
# 関数の内側で定義するようなコードを書くと
間隔 = 新しい間隔
# 4. 例外が発生します
間隔を設定する(2) # print 関数は実行されない
# 結果
# UnboundLocalError:
# cannot access local variable '間隔'
# where it is not associated with a value
#
# 直訳
# 未束縛ローカルエラー:
# ローカル変数 '間隔' にアクセスできません、
# ローカル変数 '間隔' が値に関連付けられていない状態で
#
# 意訳
# 代入される前のローカル変数を参照してしまったエラー:
# ローカル変数 '間隔' に値が代入されていない状態で
# ローカル変数 '間隔' にアクセスできません
#
# 解釈
# エラー文を見る限り global を使わずに関数内で変数 '間隔' に代入を行うコードを書くと
# 関数内では変数 '間隔' はローカル変数として取り扱われるということでしょうか...?
間隔 # 1
-
UnboundLocalError
- どこで例外が発生するの?
- 関数定義文
def 間隔を設定する(新しい間隔):
ではなく - 関数呼び出し
間隔を設定する(2)
で例外が発生します
- 関数定義文
- 関数は実行されないので
print(間隔)
は実行されません
- どこで例外が発生するの?
【Pythonクイズ】
— けむにく@競プロ (@kemuniku) May 20, 2024
このコードを実行すると、エラーが発生します。何行目でどのような原因で、どのようなエラーが発生するでしょう? pic.twitter.com/WRnsy7iELY
2. nonlocal 文
-
nonlocal
っていつ使うの?- わざわざクラス定義文を使うのが面倒だなってとき
かなと個人的に思っています。うまく説明できないのですが...orz
Step 1. nonlocal 文で実装
#
# 対話モード >>> に
# コピペで実行できます。
#
間隔 = 1 # クラス変数, 一番外側の変数
def 間隔を設定する(新しい間隔):
global 間隔
間隔 = 新しい間隔
def カウンターを作る():
現在の値 = 0 # インスタンス変数, 外側の変数
def カウントする():
nonlocal 現在の値
現在の値 = 現在の値 + 間隔
return 現在の値
return カウントする
カウントするA = カウンターを作る()
カウントするA() # 1
カウントするA() # 2
カウントするA() # 3
間隔を設定する(2)
カウントするA() # 5
カウントするA() # 7
カウントするA() # 9
カウントするB = カウンターを作る()
間隔を設定する(3)
カウントするB() # 3
カウントするB() # 6
カウントするB() # 9
間隔を設定する(4)
カウントするB() # 13
カウントするB() # 17
カウントするB() # 21
カウントするA() # 13
カウントするA() # 17
カウントするA() # 21
Step 2. nonlocal 文で実装
ちょっとクラスっぽく実装したいな...
#
# 対話モード >>> に
# コピペで実行できます。
#
間隔 = 1 # クラス変数, 一番外側の変数
def カウンターを作る():
現在の値 = 0 # インスタンス変数, 外側の変数
def 間隔を設定する(新しい間隔):
global 間隔
間隔 = 新しい間隔
def カウントする():
nonlocal 現在の値
現在の値 += 間隔
return 現在の値
return {
'カウントする': カウントする,
'間隔を設定する': 間隔を設定する
}
# インスタンスを作成して使用
カウンターA = カウンターを作る()
カウンターA['カウントする']() # 1
カウンターA['カウントする']() # 2
カウンターA['カウントする']() # 3
カウンターA['間隔を設定する'](2)
カウンターA['カウントする']() # 5
カウンターA['カウントする']() # 7
カウンターA['カウントする']() # 9
カウンターB = カウンターを作る()
カウンターB['間隔を設定する'](3)
カウンターB['カウントする']() # 3
カウンターB['カウントする']() # 6
カウンターB['カウントする']() # 9
カウンターB['間隔を設定する'](4)
カウンターB['カウントする']() # 13
カウンターB['カウントする']() # 17
カウンターB['カウントする']() # 21
カウンターA['カウントする']() # 13
カウンターA['カウントする']() # 17
カウンターA['カウントする']() # 21
なんやクソコードみせんなこの野郎😡って感じですが、この Step 2 の書き方は Step 3 と比較して self
を使っていないことを心の片隅においておいてもよいかなと感じました... React の関数コンポーネントの書き方はこの Step 2 の書き方と似ています... 伝わるかはわからないのですがあとで React の関数コンポーネントのコードを示します...
Step 3. クラス定義文で実装
クラスで実装したいな...
#
# 対話モード >>> に
# コピペで実行できます。
#
class カウンタークラス:
間隔 = 1 # クラス変数
def __init__(self):
self.現在の値 = 0 # インスタンス変数
@classmethod
def 間隔を設定する(cls, 新しい間隔):
cls.間隔 = 新しい間隔
def カウントする(self):
self.現在の値 += カウンタークラス.間隔
return self.現在の値
# インスタンスを作成して使用
カウンターA = カウンタークラス()
カウンターA.カウントする() # 1
カウンターA.カウントする() # 2
カウンターA.カウントする() # 3
カウンターA.間隔を設定する(2)
カウンターA.カウントする() # 5
カウンターA.カウントする() # 7
カウンターA.カウントする() # 9
カウンターB = カウンタークラス()
カウンターB.間隔を設定する(3)
カウンターB.カウントする() # 3
カウンターB.カウントする() # 6
カウンターB.カウントする() # 9
カウンターB.間隔を設定する(4)
カウンターB.カウントする() # 13
カウンターB.カウントする() # 17
カウンターB.カウントする() # 21
カウンターA.カウントする() # 13
カウンターA.カウントする() # 17
カウンターA.カウントする() # 21
4. そのほかの言語 - self を書かない動向
(1) Kotlin
Kotlin はクラス定義文の中で this
, Python でいうところの self
が省略できるらしいです。
(2) Swift
Swift もまたクラス定義文の中で self
が省略できるらしいです。そしてそれが主流らしいです。
本記事の結論はselfを書くべき、としていますが、現時点では逆の結論に至っています。それは下記の理由から:
- closureにキャプチャされているselfを見つけやすくするため。
- GitHubなどをみる限り、ポピュラーなほうはselfを省略するスタイル。
(3) JavaScript
React でコンポーネントという HTML の塊みたいなものを定義するときにクラスではなく関数で定義するようになりました。理由は this
, Python でいうところの self
を書くのが面倒になったからなのかなと思っています。
React の関数で定義されたコンポーネントは初見で見ると「?」ってなるので焦るのですが self
って書くのが面倒だったのかなくらいに抑えておけばよいのかなと思っています... 小並感
React におけるコンポーネントの定義
クラスコンポーネントよりも関数コンポーネントのほうが短くなっているよねっていうのがポイントです。
クラス ... Step 3 的な書き方
this.setState
で this.state
, Python でいうところのインスタンス変数を全部更新しているのがポイント。なんで一部のプロパティだけでなくすべてのプロパティを更新しているのかは知りません。
関数 ... Step 2 的な書き方
const [現在の値, set現在の値] = useState(0);
は所謂 getter と setter です。setter が起動すると値の更新を通知するようになっている はず です。
See the Pen nonlocal - 関数コンポーネント by domodomodomo (@nico25) on CodePen.
↓ useState は本当になにやっているかわからない...
もうちょっと踏み込むと
— ゆきうさぎ@フリーのシステム屋 (@__snow_rabbit__) June 14, 2024
React hooksは宇宙なんだよなー。
なかのコードをちゃんと読んでないので、
勉強不足なだけなんだと思うが、
例えば公式の⬇
const [index, setIndex] = useState(0);
これを見た時に変物と関数がセットになっているが
裏のコードが想像出来ん。
↓「クラスを使わない」ことが必ずしも「オブジェクト指向ではない」ことを表しているわけではないよね、という話をされているのだと思います。
そう!たまたまクラスが
— honey32 (@honey321998) June 14, 2024
ライフサイクルによってスライスされてしまうので、コンポーズ困難である
という致命的な弱点を抱えているだけで、オブジェクト指向(の一部の側面というのが正しい?)の考え方が全く入る余地がないとは思えない… https://t.co/T4PrH8Dn4M
◯ まとめ
バックエンドはわかりませんがフロントエンド, UI については self
を書かない流れなのかなと感じました。
おわりに
関数の中で定義する関数の使いどころとしては、他にも「デコレータ」と「カリー化, 部分的適用」があります。