Reputation: 9
I was trying to solve a dynamic programming problem in Pyhon 3.x. I wanted to create a memo dictionary object. After assigning default value of None
to memo parameter, the check for None
with not object
doesn't seem to work, but if I change it to object is None
, it works fine.
By doesn't work, I mean this condition doesn't evaluate to True
in case of not object
, and the following indented code isn't executed.
This results in TLE on LeetCode. But using is None
check does work, and it assigns a dictionary to memo, and thus caching works as intended, and the solution passes.
Why does this happen?
def lcs(i, j, memo = None):
if not memo:
memo = {}
vs
def lcs(i, j, memo = None):
if memo is None:
memo = {}
I called the function with return lcs(0, 0)
.
Edit: On my system Python IDLE, both expressions evaluate to True. So I think, the LeetCode platform handles the code differently.
Edit 2: sometime after @buran pointed out, these partial snippets were producing the expected result on LeetCode too, I also tried the partial snippets, and indeed they were giving the expected result. But then when I tried the whole code(used from past submission), surprisingly it also gave the expected result (new memo was actually created). I wasn't able to actually reproduce the problem I faced earlier until today. for a different problem, the code is again showing the same behavior, Now I think I am missing something here. That's why I'm posting the whole code this time. Can someone spot and explain why both these codes give different result.
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
def lps(l, r, memo = None):
if memo is None:
memo = {}
if (l, r) not in memo:
if l > r:
memo[(l, r)] = 0
elif l == r:
memo[(l, r)] = 1
elif s[l] == s[r]:
memo[(l, r)] = lps(l+1, r-1, memo) + 2
else:
memo[(l, r)] = max(lps(l+1, r, memo), lps(l, r-1, memo))
return memo[(l, r)]
return lps(0, len(s)-1)
vs
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
def lps(l, r, memo = None):
if not memo:
memo = {}
if (l, r) not in memo:
if l > r:
memo[(l, r)] = 0
elif l == r:
memo[(l, r)] = 1
elif s[l] == s[r]:
memo[(l, r)] = lps(l+1, r-1, memo) + 2
else:
memo[(l, r)] = max(lps(l+1, r, memo), lps(l, r-1, memo))
return memo[(l, r)]
return lps(0, len(s)-1)
Can someone please also tell if and what should I remove from my past edits or the original post. This is becoming a really long post. (I'm new here)
Upvotes: 0
Views: 846
Reputation: 77377
From Truth Value Testing
By default, an object is considered true unless its class defines either a bool() method that returns False or a len() method that returns zero.
An empty dict
has length zero, so is False
. If you call lcs
with a memo dictionary, but that dictionary doesn't happen to have anything in it, it will test False
and if not memo: memo = {}
will create a new memo object for you. That code can't tell the difference between an empty memo and None.
The if memo is None
is the proper way to assign a memo only if the caller hasn't passed one in.
Upvotes: 1
Reputation: 109
not
is a keyword which is equivalent to !=
. So it has totally different meaning.
You can use not
keyword as below (for example)
def lcs(i, j, memo = None):
if memo is not None:
memo = {}
The above code is equivalent to
def lcs(i, j, memo = None):
if memo != None:
memo = {}
If you want to use not
keyword that badly, then use this code.
def lcs(i, j, memo = None):
if not memo == None:
memo = {}
If you find have any doubts, let me know.
Upvotes: 1
Reputation: 432
It's been awhile since I've used Python so bear with me but I think I can explain the general programming concept.
In the first one you are checking if memo doesn't exist.
if not memo
Meaning to continue if memo does not exist.
In the second one you are checking that memo does exist and that it is set to None.
if memo is None
To continue if memo exists (a pre-condition to going into the check) and it is set to None
Upvotes: 0