Pythonの変数宣言

ちょっと下記のコードを動かしてみた。

#!/usr/bin/evn python
# -*- coding: utf-8 -*-

def fileopen(filename):
  try:
    f = open(filename, "rb", 0)
    try:
      f.seek(-128, 2)
      data = f.read(128)
    finally:
      f.close()
  except IOError:
    print("IOError")

if __name__ == "__main__":
  import sys
# filename = sys.stdin.read(1)
  filename = sys.argv[1]
  fileopen(filename)

コメントの所はちょっとしたメモね。上記のコードで驚いた。処理は引数でファイル名を渡してファイルの末尾から128byte読み込むってだけの処理なんだが、驚いたのは例外処理の事。空ファイル(0byte)を引数に渡すとどうなるか?OPENは行われるので例外は発生しない。でSEEKの段階で例外が発生するんだが、その例外の捕捉はSEEKの外にあるEXCEPTで捉えているんだが・・・これって当たり前なの?Javaもそうだっけ?違った気がするんだが、考え方はわからんこともない。内で発生した例外も外で捕捉できるようにしているわけだよね。ちょっと驚いた。

で、更に驚いたのは、コードを下記のように変えてみた。まあ「data」を表示してみたいと考えて何も考えず「print(data)」を一番外の「try〜except」の直後に追加したんだよね。

#!/usr/bin/evn python
# -*- coding: utf-8 -*-

def fileopen(filename):
  try:
    f = open(filename, "rb", 0)
    try:
      f.seek(-128, 2)
      data = f.read(128)
    finally:
      f.close()
  except IOError:
    print("IOError")
  print(data)

if __name__ == "__main__":
  import sys
# filename = sys.stdin.read(1)
  filename = sys.argv[1]
  fileopen(filename)

正常に動くんですよ。エラーも発生せず。もちろん引数で渡すファイルは128byte以上ですけどね。これって良く見ると不思議じゃないですか?「data」って変数は2つ目のtryの中で宣言されていますよね。この「data」は、この「try〜finally」のブロックのみで有効だと思いませんか?であるならば、fileopen関数の最後の行にある「print(data)」でエラーになるはず、だと思うのだが・・・そのようではないですね。こういった場合、他の言語では「print(data)」と同じ層に変数を宣言しておくものだと思ったのだが・・・そうですよね。どのようなメモリマップ上に変数を格納しているのか理解できていないが不思議だ。

「try〜except」の中だからですか?そこはネストしているが本当はトップレベルの層と同じとか?そんなわけ無いよね〜でも気になるので、

>>> def test():
...     if 1:
...             data = 9999
...     print(data)
...
>>> test()
9999

IF文のブロックで囲い、その中で変数を初期化。正常に動作する。もちろん初期化を行わなければエラーとなるんだが・・・不思議だ。

不安になったのでPerlで確認。

#!/usr/bin/perl

use strict;
use warnings;

if (1) {
  my $data = 9999;
}

print $data;
exit;

実行してみる。

$ ./sample001.pl
Global symbol "$data" requires explicit package name at sample001.pl line 10.
Execution of sample001.pl aborted due to compilation errors.

これが普通?だよね。これが自然というか今までやってきたやつだよ。すっきりする。動作させるには下記のように修正。

#!/usr/bin/perl

use strict;
use warnings;

my $data = 0;
if (1) {
  $data = 9999;
}

print $data;
exit;

Pythonは不思議だ。関数の内部変数は全て同じメモリ空間に保存されるって事なんだろうな〜それが同列ってわけか!ん〜これはメリット、デメリットあると思うな〜慣れるまでは絶対にデメリットの方が大きい気がする。大きなコードになると変数がどこでどのように宣言され初期値を与えられているのかわからなくなりそうだし、思いがけずに変数の値を上書きしそうだよね。不思議だ。