I come to this code in a LeetCode problem
class Solution:
def maxProduct(self, root):
self.res = total = 0
def s(root):
if not root: return 0
left, right = s(root.left), s(root.right)
self.res = max(self.res, left * (total - left), right * (total - right))
return left right root.val
total = s(root)
s(root)
return self.res % (10**9 7)
I change self.res
to res
and the code breaks with UnboundLocalError: local variable 'res' referenced before assignment
. Why res
has to be initialized as self.res
while total
does not?
CodePudding user response:
The problem is that the nested function s
has it's own variable scope.
While it can read variables (like res
) from the enclosing scope (maxProduct
),
any assignment (e.g. res = max(...)
)
will introduce a variable in the scope of s
, instead of
writing to the outer (res
).
Because in this case maxProduct
wants to read the modified version of res
that s
produced, the trick of adding a member variable to self is used.
(In that case, s
only reads the reference to self, and then adds a member, avoiding the scope issue).
See this little example for demonstration:
def foo():
def bar():
x=2
x=1
bar()
return x
foo()
returns 1, not 2, because, the assignment to x
in bar
introduces a new variable in the scope of bar
CodePudding user response:
Why res has to be initialized as self.res while total does not?
It does not, but because of the rules of Python's implicit declarations, by default assignment is declaration.
Since s
only reads from total
, it necessarily has to come from somewhere else, but because of
res = max(self.res, left * (total - left), right * (total - right))
Python will create a res
variable which is local to s
, so you really have two different variables with the same name, one in maxProduct
and one in s
. This is called shadowing.
You can tell Python that a variable being assigned to is not a local by explicitly declaring it so:
nonlocal res
This will make Python assign to the lexically closest variable of that name:
def foo():
a = 1
def bar():
a = 2
bar()
print(a)
foo()
# 1
def foo():
a = 1
def bar():
nonlocal a
a = 2
bar()
print(a)
foo()
# 2