pythonのデフォルト引数の挙動は可怪しいという話
Pythonをすごいやりたい!ってわけではないんですが、DiscordのBot作るのにPython便利そうなので使ってます。
で、プログラミング言語にはデフォルト引数っていう機能がありますよね?Pythonにもあるんですが、Pythonのデフォルト引数の仕様は普段私が使ってる言語たち(Ruby, CL, Scala etc)と仕様がちょっと違うのでハマりましたよという話。
import time import datetime class Foo: def bar(self, now = datetime.datetime.now()): print(f"{now}") hoge = Foo() while True: hoge.bar() time.sleep(1)
ここでbar
っていうFooクラスのインスタンスメソッドがあったとして、普通bar
が呼ばれるたびにnow
は更新されると思いませんか?。ところがpythonってそうならないんですよね。。。classがnewされたときのnowが固定で代入されてずーっと呼び出されます。
私このPythonの意味不明仕様をしらなかったもんで、これに1時間ぐらいハマりました。ユニットテストでは通るのに、実際にコードを動かすと思ったように動かない。
他の言語ではどうなんですか?っていう話ですが
例えばRuby
require 'date' class Foo def bar(now = Time.now) puts now end end hoge = Foo.new() while true hoge.bar() sleep(1) end
Rubyの結果
2020-12-29 15:47:16 +0900 2020-12-29 15:47:17 +0900 2020-12-29 15:47:18 +0900 2020-12-29 15:47:19 +0900 2020-12-29 15:47:20 +0900 2020-12-29 15:47:21 +0900 2020-12-29 15:47:22 +0900 2020-12-29 15:47:23 +0900 ^CTraceback (most recent call last): 1: from test.rb:14:in `<main>' test.rb:14:in `sleep': Interrupt
普通はこうですよね!メソッド呼び出しのたびにデフォルト引数内部は更新されますよ!変わりますってば!
Scalaの場合
class Hoge { def _now() { } } class Foo { def bar(now: Hoge = new Hoge()) { println(now) } } val hoge = new Foo() while (true) { Thread.sleep(1000) hoge.bar() }
Main$$anon$1$Hoge@16f65612 Main$$anon$1$Hoge@311d617d Main$$anon$1$Hoge@7c53a9eb Main$$anon$1$Hoge@ed17bee Main$$anon$1$Hoge@2a33fae0 Main$$anon$1$Hoge@707f7052 Main$$anon$1$Hoge@11028347 Main$$anon$1$Hoge@14899482 Main$$anon$1$Hoge@21588809 Main$$anon$1$Hoge@2aae9190 Main$$anon$1$Hoge@2f333739 Main$$anon$1$Hoge@77468bd9 Main$$anon$1$Hoge@12bb4df8
Scalaの場合もそうです。呼び出しごとに更新されます。当たり前です。
Common Lispの場合
(let ((foo ())) (defun bar (&optional (now (random 100))) (print now))) (loop (progn (bar) (sleep 1)))
14 69 6 48 60 73 8 31 43 4 6 71 14 81 78 14
いや普通(世の中に出回ってる言語の多数派)こうだと思いますが。。。はて。。。?
Pythonってこの他にも「なぜこうした?」っていう意味不明な仕様が多くて、例えば、map
やfilter
といった関数もmap object
とかfilter object
とかが返ってきたりしますよね?
いやこういうのって、普通基底クラスなりなんなりにEnumerable
とかTraversable
みたいなのがあって、ArrayとかListとかはそいつを継承させるものなんじゃないんですか?だからmap objectとかfilter objectとか別途用意するんじゃなくて、mapの結果が直接ArrayとかListを出せるようにするべきなんじゃないんですかね?とかとか