Python nonlocal statement
Compare this, without using nonlocal
:
x = 0
def outer():
x = 1
def inner():
x = 2
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 1
# global: 0
To this, using nonlocal
, where inner()
's x
is now also outer()
's x
:
x = 0
def outer():
x = 1
def inner():
nonlocal x
x = 2
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 2
# global: 0
If we were to use global
, it would bind x
to the properly "global" value:
x = 0
def outer():
x = 1
def inner():
global x
x = 2
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 1
# global: 2
When to use nonlocal keyword?
What you are facing there is that in one case, you have in the vareiable a mutable object, and you operate on the object (when it is a list) - and in the other case you are operating on an imutable object and using an assignment operator (augmented assingment +=
) .
By default, Python locates all non-local variables that are read and use then accordingly - but if a variable is assigned to in the inner scope, Python assumes it is a local variable (i.e. local to the inner function), unless it is explicitly declared as nonlocal.
Can I get the value of a non-local variable without using the nonlocal statement?
I don't get your context that you have to use same name.
Anyway, you can capture outer function's locals as nonlocal variable.
x = "global"
def f(x="nonlocal"):
nonlocals = locals()
def g():
x = "local"
print(x)
print(nonlocals['x'])
print(globals()["x"])
return g
f()()
output:
local
nonlocal
global
Python's nonlocal keyword - is this good practice?
I think that under the circumstances you describe, the nonlocal
approach objectively makes more sene than the other one you show. Your goal is to have a counter outside your function and a way to keep track of the result when it is found regardless of where you are in the recursion.
nonlocal
completely encapsulates that information in a namespace dedicated to the particular run of your outer function. There is really no downside here.
Using instance attributes makes that information available to anyone using the instance. This is unnecessary conceptually, marginally slower, and not thread-safe. While thread safety is often not a concern in Python, it does make your code much less robust. Between that and encapsulation, I would definitely stay away from this approach.
I would suggest a third possibility here: using return values. In that case, your really don't need any external namespace. I don't necessarily recommend this over using nonlocal
, but it's worth mentioning. Here is a sample implementation, which as you can see is much more verbose than your solution:
class Solution:
def kthSmallest(self, root: TreeNode, k: int) -> int:
def traverse_inorder(root, target):
if not root:
return 0, None
left_count, item = traverse_inorder(root.left, target)
if left_count == target - 1:
return left_count + 1, root.val
elif left_count < target:
right_count, item = traverse_inorder(root.right, target - left_count - 1)
return left_count + right_count + 1, item
else: # left_count == target
return left_count, item
count, ans = traverse_inorder(root, k)
if count < k:
raise ValueError('Insufficient elements')
return ans
There are many ways to do this with return values. Here, I compute the number of elements in each subtree, up to the target value. A non-none item is only returned if the exact number of elements is found.
Python nonlocal statement in a class definition
Lexical scoping applies only to function namespaces, otherwise methods defined inside a class would be able to "see" the class level attributes (which is by design - those attributes must instead be accessed as attributes of self
inside the method).
The same limitations that cause the class level variables to be skipped over by references from methods also keep the nonlocal
keyword from working its magic. (global
does work though, since that doesn't rely on the lexical scoping machinery)
using nonlocal or global inside a class body
nonlocal
wouldn't work in any event, because variables have only one scope in the case where nonlocal
applies (function locals, which are subtly different from class definition scope); by trying to use nonlocal
, you'd say model
was never part of the class definition scope, just something from outside it.
I personally prefer your kinda hacky reassignment so _model
outside the class and model
inside the class don't conflict, but if you hate it, there is an option to directly access the class-in-progress's namespace, vars()
(or locals()
; the two are equivalent in this case, but I don't think of the class scope as being locals, even though they act a lot like it).
Because the scope is not really a function scope, you can in fact mutate it through the dict
from vars
/locals
, allowing your desired result to look like:
def generate_meta_options(model, design):
class Meta:
vars().update(
model=model,
design=translate_old_design_spec_to_new_format(design)
)
return Meta
Using the keyword argument passing form of dict.update
means the code even looks mostly like normal assignment. And Python won't complain if you then use those names earlier (seeing outer scope) or later (seeing newly defined names) in the class definition:
def generate_meta_options(model, design):
print("Before class, in function:", model, design) # Sees arguments
class Meta:
print("Inside class, before redefinition:", model, design) # Sees arguments
vars().update(
model=model,
design=design+1
)
print("Inside class, after redefinition:", model, design) # Sees class attrs
print("After class, in function:", model, design) # Sees arguments
return Meta
MyMeta = generate_meta_options('a', 1)
print(MyMeta.model, MyMeta.design)
Try it online!
syntax error on nonlocal statement in Python
nonlocal
only works in Python 3; it is a new addition to the language.
In Python 2 it'll raise a syntax error; python sees nonlocal
as part of an expression instead of a statement.
This specific example works just fine when you actually use the correct Python version:
$ python3.3
Python 3.3.0 (default, Sep 29 2012, 08:16:08)
[GCC 4.2.1 Compatible Apple Clang 3.1 (tags/Apple/clang-318.0.58)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def outer():
... x = 1
... def inner():
... nonlocal x
... x = 2
... print("inner:", x)
... inner()
... print("outer:", x)
...
Related Topics
Import Module from String Variable
How to Input a Regex in String.Replace
Refering to a Directory in a Flask App Doesn't Work Unless the Path Is Absolute
Python'S Many Ways of String Formatting - Are the Older Ones (Going to Be) Deprecated
Equivalent to Time.Sleep For a Pyqt Application
Numpy or Pandas: Keeping Array Type as Integer While Having a Nan Value
Sorting Arrays in Numpy by Column
Convert All Strings in a List to Int
Determine the Type of an Object
Python Exit Commands - Why So Many and When Should Each Be Used
Speed Up Millions of Regex Replacements in Python 3
Process Escape Sequences in a String in Python
Is There a Portable Way to Get the Current Username in Python
Scatter Plot With Different Text At Each Data Point
Variable Scopes in Python Classes
How to Test If a String Contains One of the Substrings in a List, in Pandas