1 回答

TA貢獻(xiàn)1752條經(jīng)驗(yàn) 獲得超4個(gè)贊
(這是對現(xiàn)在已從最初問題中編輯出來的代碼的回應(yīng))您忘記在第二種情況下調(diào)用函數(shù)。進(jìn)行適當(dāng)?shù)男薷?,結(jié)果如預(yù)期:
test1 = """
def foo1():
my_set1 = set((1, 2, 3))
foo1()
"""
timeit(test1)
# 0.48808742000255734
test2 = """
def foo2():
my_set2 = {1,2,3}
foo2()
"""
timeit(test2)
# 0.3064506609807722
現(xiàn)在,時(shí)間差異的原因是因?yàn)閟et()函數(shù)調(diào)用需要查找符號表,而{...}集合構(gòu)造是語法的人工制品,并且速度要快得多。
觀察反匯編的字節(jié)碼時(shí),差異很明顯。
import dis
dis.dis("set((1, 2, 3))")
1 0 LOAD_NAME 0 (set)
2 LOAD_CONST 3 ((1, 2, 3))
4 CALL_FUNCTION 1
6 RETURN_VALUE
dis.dis("{1, 2, 3}")
1 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (2)
4 LOAD_CONST 2 (3)
6 BUILD_SET 3
8 RETURN_VALUE
在第一種情況下,函數(shù)調(diào)用是由CALL_FUNCTION元組上的指令進(jìn)行的(1, 2, 3)(它也有自己的開銷,雖然很小——它通過 加載為常量LOAD_CONST),而在第二條指令中只是一個(gè)BUILD_SET調(diào)用,這更多高效的。
回復(fù):您關(guān)于元組構(gòu)建所需時(shí)間的問題,我們認(rèn)為這實(shí)際上可以忽略不計(jì):
timeit("""(1, 2, 3)""")
# 0.01858693000394851
timeit("""{1, 2, 3}""")
# 0.11971827200613916
元組是不可變的,因此編譯器通過將其作為常量加載來優(yōu)化此操作——這稱為常量折疊(您可以從LOAD_CONST上面的指令中清楚地看到這一點(diǎn)),因此所花費(fèi)的時(shí)間可以忽略不計(jì)。這在集合中看不到,因?yàn)樗鼈兪强勺兊模ǜ兄x@user2357112 指出這一點(diǎn))。
對于更大的序列,我們看到了類似的行為。{..}與set()必須從生成器構(gòu)建集合相比,使用集合推導(dǎo)式構(gòu)造集合的語法更快。
timeit("""set(i for i in range(10000))""", number=1000)
# 0.9775058150407858
timeit("""{i for i in range(10000)}""", number=1000)
# 0.5508635920123197
作為參考,您還可以在更新的版本上使用可迭代解包:
timeit("""{*range(10000)}""", number=1000)
# 0.7462548640323803
然而,有趣的是,set()直接調(diào)用時(shí)速度更快range:
timeit("""set(range(10000))""", number=1000)
# 0.3746800610097125
這恰好比集合構(gòu)造更快。您將看到其他序列(例如lists)的類似行為。
我的建議是{...}在構(gòu)造集合文字時(shí)使用集合理解,并作為將生成器理解傳遞給的替代方法set();而是用于set()將現(xiàn)有序列/可迭代對象轉(zhuǎn)換為集合。
添加回答
舉報(bào)