« 新年会日記 |Main| 一日遅れ日記 »

« ブログにコードを貼り付ける方法.js alpha0.2(Firefox only) | JavaScript | jQuery勉強日記 »

« circular doubly linked listを使って0/1配列を省メモリ実装 | Python | 差分リストを作るには »

PythonとJavaScriptの微妙な違い

だいありー

なんかささださんと話がかみ合っていない気配がするので、 とりあえずこちらが知っているPythonの話をまとめてみます。

Pythonの場合、関数内で代入が行われる変数はローカル変数です。 すでに外のスコープに変数があろうがそんなことはお構いなしにローカル変数です。 例外は、変数をglobal宣言した場合で、この場合だけグローバル変数になります。

>>> def func1():
	global a
	a = 1 # グローバル変数への代入になっている
	b = 2
	print locals()

	
>>> func1()
{'b': 2} # ローカル変数はbだけ
>>> a
1 # aはグローバル変数なのでアクセスできる
>>> def func2():
	a = 1
	global a
	
SyntaxError: name 'a' is assigned to before global declaration (, line 1)
それでもって、一つの関数内で同じ名前でグローバル変数とローカル変数を作ったら、 グローバル変数がローカル変数で隠されている状態になるのが自然だと僕は思ったわけです。
>>> def func2():
	c = 1 # ローカル変数作成
	globals()["c"] = 2 # グローバル変数作成
	print c

	
>>> func2()
1 # ローカルのcは1
>>> c
2 # グローバルのcは2
でもって、ささださんの挙げたJavaScriptの例も、 "a"になるのが自然だな、でも違うのか、と思って試してみたらやっぱり"a"になったので、 「うむむ、これは何か話がかみ合ってないぞ?」と思った次第。
function test() {
  var foo = "a";
  function inner() {
    foo = "b";
    var foo = "c";
  }
  inner();
  return foo;
}
document.write(test());
PythonでもJavaScriptと同じように"a"が返ります。
>>> def test():
	foo = "a"
	def inner():
		globals()["foo"] = "b"
		foo = "c"
	inner()
	return foo

>>> test()
'a'
ささださんが何を不自然だと感じたのか詳しいことはわからないですけど、 もし"b"が返ると思ったのだったらどういうことか考えてみると…。 きっと僕の脳内では
  • JavaScriptの"x = 1"はグローバル変数のxを作成して1で初期化する命令
  • グローバルスコープとは、トップレベルのスコープ
という認識になっていて、
  • 「foo = "b";」によってグローバル変数がいじられようが何だろうが、 関数testのローカル変数が影響を受けるはずがない
  • 「var foo = "c";」はローカル変数だから関数の外に影響を及ぼすはずがない。
と感じるのでしょう。 一方、"b"が返るべきだと感じる人は
  • JavaScriptの"var x"は変数xの有効範囲を関数内に限定する命令
  • varの付いていないのは限定されていない変数
  • 限定されていないってのはつまり、1枚外側のスコープってこと
という認識になっているのでしょう。 そういう実装の言語もありだと思います。 で、その場合
  • 「var foo = "c";」はローカル変数だから関数の外に影響を及ぼすはずがない。
  • でも「foo = "b";」は普通の変数なんだから、「var foo = "a";」と同じスコープ。
  • あれ?"a"って返ってくるぞ?
となるんじゃないでしょうか。

と勝手に憶測。

でもなんだかんだ考えた結果、JavaScriptもPythonと同じように 「varでスコープを限定したいのなら、最初の代入の時までに宣言しなければシンタックスエラー」 というようになればいいんじゃないかと思えてきました。 というわけで「え、エラー出すべきなの?」は撤回。


= うっ、これは後に代入された方が返るのか…。 これは気持ちが悪い…。当然"b"が返ると思ったのに…。
function inner() {
  var foo = "b";
  foo = "c";
  document.write(foo);
}
inner();
Pythonなら代入の順序にかかわらず"b"が返ります。
>>> def test():
	globals()["foo"] = "c"
	foo = "b"
	print foo
	foo = "b"
	globals()["foo"] = "c"
	print foo

	
>>> test()
b
b

ふうむ。 Core JavaScript 1.5 Guide:Variables - MDC: 「JavaScript の変数に関して独特なこととして、後に宣言される変数を例外を発生させることなく参照できるということもあります。」 っていう実装なのか。 仮に関数の冒頭でfoo = 1と書いてあっても、 もしかすると関数の最後にvar fooと書いてあるかも知れないから 全部読むまではグローバル変数かどうかわからないわけか。 一方Pythonはfoo = 1が出てきた時点でローカル変数に決定、 仮に後からglobal fooが出てきても構文エラー、と。 global fooが先に出てきたらfooはグローバル変数に決定、と。

Python流の方がいいと思うけど、 JavaScript流の方がインタプリタの実装が楽そうな気配。

トラックバック(Trackback)

Trackback URL: http://www.nishiohirokazu.org/mt/mt-tb.cgi/484

フィードバック

by ささだ | 2007年01月21日 03:08

> うっ、これは後に代入された方が返るのか…。これは気持ちが悪い…。当然"b"が返ると思ったのに…。

これは、それこそ普通だと思う。つまりCと同じ。Schemeと同じ。

Pythonの場合は「外を明示的に指定」しているので迷うことは無いわけです。ってゆーか、グローバルとローカル、の違いはアンフェアだよ。Rubyだって$foo って書けば必ずグローバルだから。

インナーとアウターで使っている「ローカル変数」の話なので、ちょっとこのPythonの例は違うのではないだろうか。globals() ってのが、いわゆるグローバル変数を管理するためのものでなければ。

問題の本質は、foo=1; var foo=2; としたとき、前の foo は「何か」ということが曖昧だということだと思う。C++的解釈もあれば、JavaScript的解釈もある。それを許さないLisp的なものなんかもある(そういえば、Javaはどうだろうな)。

Perlはどうなんだろう、とふと思った。

ご意見・ご感想をお送りください(フィードバック)

(フィードバックはメールで送信され、基本的に表示されませんが、内容によっては公開させていただくこともございます。ご了承ください。Your comment doesn't appear the page immediately. If the comment has value to other people, it will be put on the page or subsequent entries. Thank you.)

上の情報は、いずれも未記入でかまいません。 All of above questions are optional.