This is a memo of O'Reilly Japan's book effective python. https://www.oreilly.co.jp/books/9784873117560/ P31~35
** Sort example ** Sort the numbers in order, but suppose you have a number you want to prioritize
def sort_priority(values, group):
def helper(x):
if x in group:
return (0, x)
return (1, x)
values.sort(key=helper)
numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}
sort_priority(numbers, group)
print(numbers)
>>>
[2, 3, 5, 7, 1, 4, 6, 8]
If you don't know the concept of closures, it's hard to imagine how they work. There are three points here
** Incorporate to determine if it is included in the priority list ** Based on the above code, suppose you want to implement an implementation that returns True if it is included in the priority list.
def sort_priority2(numbers, group):
found = False
def helper(x):
if x in group:
found = True #It should be True here. .. ..
return(0, x)
return(1, x)
numbers.sort(key=helper)
return found
found = sort_priority2(numbers, group)
print('Found', found)
print(numbers)
>>>
Found False
[2, 3, 5, 7, 1, 4, 6, 8]
Originally, Found should be True in the calculation, but for some reason it returns False. The reason for this is the closure's ** scope **.
The above code does not return False because the scope of Found stays outside the helper function. In other words
def sort_priority2(numbers, group):
found = False #Because found exists in the scope here. ..
def helper(x):
if x in group:
found = True #Don't go to the scope here
return(0, x)
return(1, x)
numbers.sort(key=helper)
return found
As a result, False that is one scope higher is returned. To avoid this, python3 provides a ** nonlocal ** function. nonlocal pushes the scope out of the closure
def sort_priority3(numbers, group):
found = False
def helper(x):
nonlocal found #Here the scope of found is out of the helper function
if x in group:
found =True
return(0, x)
return(1, x)
numbers.sort(key=helper)
return found
found = sort_priority3(numbers, group)
print('Found', found)
print(numbers)
>>>
Found True
[2, 3, 5, 7, 1, 4, 6, 8]
This is the intended movement.
However, if you use nonlocal in a large-scale function, it will affect the range of unintended parts, so be careful. If you want to be on the safe side, it's better to wrap it in a similar class instead of the nonlocal function.
class Sorter(object):
def __init__(self, group):
self.group = group
self.found = False
def __call__(self, x):
if x in self.group:
self.found = True
return(0, x)
return(1, x)
sorter = Sorter(group)
numbers.sort(key=sorter)
assert sorter.found is True
>>>
(Exception by assert does not occur)
This one can be used without worrying about the scope.
By the way, nonlocal is not supported in python2.
Recommended Posts