個人的に誤解していたので記載しておきます。
最初に結論
Pythonのwith文は、Rubyのブロック(do-end)と似ているが別物。
Rubyのブロックと違ってPythonのwith
文はスコープを生成しない。
例えばファイルを扱う処理で「自動的にファイルを閉じる」という目的が達成できるという点では同様だが、変数のスコープとしては意識しておく必要がある。
環境
OSはmacOSで確認(そもそもOSに依存しない)。
$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
$ python3 -V
Python 3.7.0
※ with
文(構文?)が使えるようになったのはPython 2.5以降らしい。
具体例
まずはRubyから。
irb(main):011:0> File.open('test.txt') do |file|
irb(main):012:1* a = 1
irb(main):013:1> end
=> 1
irb(main):014:0> a
Traceback (most recent call last):
2: from /usr/local/bin/irb:11:in `<main>'
1: from (irb):14
NameError (undefined local variable or method `a' for main:Object)
do-endの内部で定義した変数a
はブロック内のローカル変数なのでスコープの外側ではアクセスできない。
別の言い方をすると、Rubyのブロックはスコープを作る(ただし、if
やfor
などの制御構文はスコープを作らない)。
Python の場合は以下のようになる。
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: with open('test.py' ) as fp :
...: a = 1
...:
...:
In [2]: print(a)
1
with文はスコープを生成しないのでこの変数a
はローカル変数ではない。
制御構文の一種と捉えるか、try-except文(これもスコープを作らない)のシンタックスシュガーと考えておいたほうがいい。
PythonもRubyに共通して制御構文であるif
、for
、while
もスコープを生成しない(C言語系の言語は違う)。
まとめ
C言語系のスコープとRubyのブロック、このあたりの区別ができていないとコードの読解で混乱します。
他にもハマりやすいぽいんとはありますが他の記事で解説されていると思うので省略。