Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline callback functions in python

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.

like image 451
CaptainStiggz Avatar asked Mar 01 '17 22:03

CaptainStiggz


People also ask

What is an inline function 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.

What is callback function in Python?

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.

What are the types of callback?

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).

What is callback function and how it works?

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.


1 Answers

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)
like image 64
skrx Avatar answered Oct 03 '22 00:10

skrx