追記
2019/12/11追記:
コメントで時間計測の誤りをご指摘いただきました。
現在、正しいコードでの再検証中です。
TL;DR
-
[]
表記の方が実行時間で有利 -
list()
と[]
でタプルの扱いが一部異なっているので注意が必要
動機
PythonにはThere should be one-- and preferably only one --obvious way to do it.
という哲学があるが、頻繁に使われるデータ構造の一つであるリストについて、二通りの書き方があるのはこの哲学に反しているのでは?
何か違いがあって、異なる定義ができるようになっているのか?と気になった。
調べてみた
StackOverflowに2015年に同様の質問がされていた。
One's a function call, and one's a literal:
なるほど、list()
は関数であり、[]
はリテラルであると。
よくわからん。
For the sake of completion, another thing to note is that list((a,b,c)) will return [a,b,c], whereas [(a,b,c)] will not unpack the tuple. This can be useful when you want to convert a tuple to a list. The reverse works too, tuple([a,b,c]) returns (a,b,c).
なるほど、タプルに対しての挙動が異なっている。
上記の関数とリテラルであることがこのような挙動の違いに現れているのか?
Quoraでも2016年に同様の質問がされていた。
The end result is the same whether you use [ ] or list() and same for { } vs. dict(). There is a difference in performance, though. [ ] and { } are more performant than the alternatives, list() and dict(), mainly because using list() and dict() involves the overhead of symbol lookup and function invocation.
In my opinion, using [ ] and { } is more pythonic. In addition, this notation allows you to take advantage of list/dictionary comprehension.
そういえば、辞書型においてもdict()
と{}
で二通りの定義の仕方がある。
リテラル表記の方がよりPythonic(Pythonらしい)である?自分はまだまだPython全然わからんな。
検証
それぞれ一部挙動が異なっていることは判ったが、同等の挙動をするような状況ではどちらを使うのが良いのだろう?
関数とリテラルでコールに違いがあるということなので、それぞれ100回づつオブジェクトを生成するようなコードで検証してみた。
実行環境
- AWS EC2 t2.micro
- Pyhon 3.6.8
実行時間の比較
#-*- using:utf-8 -*-
import time
if __name__ == '__main__':
start = time.time()
for i in range(0,100):
exec("list_%d = %s" % (i, []))
elapsed_time1 = time.time() - start
print ("elapsed_time:{0}".format(elapsed_time1) + "[sec]")
start = time.time()
for j in range(100,200):
exec("list_%d = %s" % (j, list()))
elapsed_time2 = time.time() - start
print ("elapsed_time:{0}".format(elapsed_time2) + "[sec]")
なるほど実行時間に有意差がある!
ちなみにループ数を1000回および10000回にした場合にもリテラル[]
における実行時間の有意性が認められた。
タプルの扱い
上述したように、生成時にタプルを引数にとるとそれぞれで異なる挙動をするが、
オブジェクトを生成したあとの挙動はどうなるのか?
>>> l1 = []
>>> l1.append((1,2,3))
>>> print(l1)
[(1, 2, 3)]
>>> l2 = list()
>>> l2.append((1,2,3))
>>> print(l2)
[(1, 2, 3)]
いったんリストオブジェクトとして生成されたあとの挙動には違いがないようだ。
この辺はPythonというかオブジェクト指向の話なんだろう…オブジェクト指向理解してないなー。
リスト内包表記
Q. 実行できる?できない?
A. どちらの表記でも実行可能。