I'd like to be able to define a python function which does some sort of data structure traversal and performs some operation (defined in a callback) at each step. Here's a simple example to illustrate.
def min_value(list):
m = 0
for i in range(0, len(list)):
if list[i] < m: m = list[i]
return m
This example is trivial, but becomes more complex if I'm traversing an n-dimension array, a numpy array, or I want to define a more complex method of stepping through the data structure.
I'm looking for a "pythonic" analogue to the following JavaScript which does the same thing.
traverse = function(list, callback) {
for(i = 0; i < list.length; i++) {
callback(list[i])
}
}
min_value = function(list) {
m = 0
traverse(list, function(element) {
if(element < m) m = element
});
}
If I wanted a function max_value(list)
, it'd be handy to be able to just adjust the one line to if(element > m) m = element
without having to to copy/paste my traversal function. What's the best way to do this in python?
EDIT (THE ANSWER): Here I use the accepted answer below to show how my original JS example could be written in python 3. There are clearly better ways to find the minimum value of a list of scalars, but this is the pattern I was looking for.
def traverse(list, callback):
for i in range(0, len(list)):
callback(list[i])
def min_value(list):
m = 0
def update_min(val):
nonlocal m
if(val < m): m = val
traverse(list, update_min)
return m
EDIT (COMPLEX EXAMPLE): Here's a more complex and direct example of my use case. I have an image and I want to do some processing on a "focus region" of the image. Let's say, for example, I want to find the darkest 10x10, 20x20, 30x30, etc pixel regions of the image. My image is a numpy array. I'll use a "bastard" multiline lambda to illustrate what I'd like to do, though as far as I know lambdas cannot be used in this way.
In python:
# find the coordinates and of darkest region
def darkest_region(image, fsize):
darkest_coords = [0, 0]
darkest_avg = 256
# illegal lambda
g = lambda x, y, r:
# r is an fsize x fsize region
avg_darkness = # blah blah, traverse and average pixels
if(avg_darkness < darkest_avg): darkest_coords = [x, y]
focus_scan(image, fsize, g)
return darkest_coords
# scan all regions of fsize x fsize
def focus_scan(image, fsize, callback):
height, width = image.shape[:2]
for y in range(0, height - fsize):
for x in range(0, width - fsize):
focus = image[y:y+fsize, x:x+fsize]
callback(x, y, focus)
Given that I can't have a multiline lambda like that, I think what I need to do is get a little fancy with the arguments and return value of focus_scan
, in order to accommodate a variety of possible inputs/outputs. In this example, I could pass focus scan darkest_avg
and have it return the coordinates I'm looking for, but I might want to pass it something other than a scalar, or have it return a more complex data structure. I imagine I'll have to do something like:
def focus_scan(image, fsize, callback, args):
... # loopy loops
some_result = callback([x, y, focus], args)
return some_result
I like the inline function approach I can use in other programming languages, but it doesn't look like it's a common pattern in python.