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.
Python lambda functions, also known as anonymous functions, are inline functions that do not have a name. They are created with the lambda keyword. This is part of the functional paradigm built-in Python. Python lambda functions are restricted to a single expression.
In Python, a callback is simply a function or a method passed to LocalSolver. A callback takes two parameters: the LocalSolver object that triggers the event and the type of the callback. It is possible to use the same callback method or object for multiple events or multiple LocalSolver instances.
There are two types of callbacks, differing in how they control data flow at runtime: blocking callbacks (also known as synchronous callbacks or just callbacks) and deferred callbacks (also known as asynchronous callbacks).
A callback function is a function that is passed as an argument to another function, to be “called back” at a later time. A function that accepts other functions as arguments is called a higher-order function, which contains the logic for when the callback function gets executed.
Just use a normal function instead of the multiline lambda. To get access to the darkest_coords
variable you can use the nonlocal
keyword in Python 3. In Python 2 you have to mutate the list, because the nonlocal
keyword is not available.
def darkest_region(image, fsize):
"""Find the coordinates of darkest region."""
darkest_coords = [0, 0]
darkest_avg = 256
def g(x, y, r):
nonlocal darkest_coords
# 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
def focus_scan(image, fsize, callback):
"""Scan all regions of fsize x fsize."""
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With