为什么Python语句'如果东西是None'比'如果不是东西'要快得多?

问题描述:

编写斐波那契函数时遇到了这个问题。为了使它更快,我使用名为cache的字典来存储已经计算的数字。所以我写了为什么Python语句'如果东西是None'比'如果不是东西'要快得多?

def fib(n, cache=None): 
    if not cache: 
     cache = {} 
    if n in cache: 
     return cache[n] 
    if n==0 or n==1: 
     return 1 
    else: 
     cache[n] = fib(n-1, cache) + fib(n-2, cache) 
    return cache[n] 

def fib_run(n): 
    start = time.time() 
    fib(n) 
    end = time.time() 
    print("fib_run cost: {}".format(end-start)) 

我呼吁fib_run(30),输出为fib_run cost: 0.8419640064239502,它只是作为不带缓存的功能缓慢。 但是,当我在功能fib2中将if not cache:更改为if cache is None:时,它的工作速度更快。

def fib2(n, cache=None): 
    if cache is None: 
     cache = {} 
    if n in cache: 
     return cache[n] 
    if n==0 or n==1: 
     return 1 
    else: 
     cache[n] = fib2(n-1, cache) + fib2(n-2, cache) 
    return cache[n] 

def fib2_run(n): 
    start = time.time() 
    fib2(n) 
    end = time.time() 
    print("fib_run cost: {}".format(end-start)) 

>>> fib2_run(30) 
fib2_run cost: 2.6226043701171875e-05 

我想知道为什么两种方法之间存在如此巨大的差异(我认为它们在早期是一样的)。谢谢。

+2

'缓存None'是身份检查。 'not cache'是字典的空白检查。这些是引擎盖下的两种不同的操作。 –

+0

你的意思是空虚检查花费更多的时间?你能告诉我为什么吗? –

这里的问题不是is None VS not cache的表现,相反,它是if cache is NoneTrue仅仅在fib2if not cacheTrue即使cache == {}fib第一次调用。 “空”的集合评估为因此Falsenot一个空的集合会True

>>> not {} 
True 

所以你在做额外的工作(以{}分配缓存),即使在情况下,cache已经等于{}

一般来说,这些操作在性能上非常相似,一个是简单的身份检查,而另一个是基于值的单数(对于字典而言,基于__len__的结果,如果我不是错误)。

+0

这有助于很多,谢谢。 –

+0

@KasheemLew不客气,很高兴我能帮到你! :-) –

在第一种情况下,缓存总是空的!难怪你获得与没有缓存的功能相同的时间。所有你需要的是打印看看会发生什么。

if not cache: 
#if cache is None: 
    print(n, "No cache!") 
    cache = {} 

如上所述,空集合评估为false。在递归调用中,if not cache总是如此,你创建一个新的字典,你发送一个级别在下面,这又被评估为错误的,等等。缓存中永远不会有单个条目。

(做额外的工作,甚至在缓存中已经等于{}的情况下,如上面提到的,是一个时间的操作。不应该花费更大的ñ东西)