473,385 Members | 1,384 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Turning a callback function into a generator

Let's say I have a function that takes a callback function as a
parameter, and uses it to describe an iteration:

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)

For the sake of argument, assume the iteration is something more
interesting than this which relies on the callback mechanism. The
function is an existing interface, and I cannot change it.

I want to somehow, in some way, provide an iteration interface to this
function. Thoughts?

-Kirk McDonald
Jul 3 '06 #1
7 3576
Kirk McDonald wrote:
Let's say I have a function that takes a callback function as a
parameter, and uses it to describe an iteration:

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)

For the sake of argument, assume the iteration is something more
interesting than this which relies on the callback mechanism. The
function is an existing interface, and I cannot change it.

I want to somehow, in some way, provide an iteration interface to this
function. Thoughts?
I don't think there is a robust solution, see

http://groups.google.com/group/comp....6408134?&hl=en
aka http://mail.python.org/pipermail/pyt...er/197726.html

http://groups.google.com/group/comp....5d84694?&hl=en
aka http://mail.python.org/pipermail/pyt...er/239025.html

Peter

Jul 3 '06 #2

Peter Otten wrote:
Kirk McDonald wrote:
Let's say I have a function that takes a callback function as a
parameter, and uses it to describe an iteration:

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)
Which object is immutable? the callback or the function? If its the
callback then

def func(callback):
for i in numbers:
yield callback(i)

If the function is immutable, then its a bit harder. The callback has
to be able to do the processing. You can't use an iterator here
because call stack gets in the way. You could store the information
being passed to the callback in a list, then iterate over the list
afterwards. Or you could have the callback be able to handle all of
the work at once.

What do you intend to use this for? Python has a lot of options and
you may not be using the best one for the problem

Jul 3 '06 #3
In article <44******@nntp0.pdx.net>,
Kirk McDonald <ki**************@gmail.comwrote:
>I want to somehow, in some way, provide an iteration interface to this
function. Thoughts?
Run it in a separate thread/process?
Jul 3 '06 #4
cm************@yaho.com wrote:
Peter Otten wrote:
>>Kirk McDonald wrote:

>>>Let's say I have a function that takes a callback function as a
parameter, and uses it to describe an iteration:

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)


Which object is immutable? the callback or the function? If its the
callback then

def func(callback):
for i in numbers:
yield callback(i)

If the function is immutable, then its a bit harder. The callback has
to be able to do the processing. You can't use an iterator here
because call stack gets in the way. You could store the information
being passed to the callback in a list, then iterate over the list
afterwards. Or you could have the callback be able to handle all of
the work at once.

What do you intend to use this for? Python has a lot of options and
you may not be using the best one for the problem
It is the function that is immutable.

I am writing a library for the D programming language that is not
totally unlike Boost.Python:

http://dsource.org/projects/pyd/wiki

It is still in the fairly early stages, although the basic function and
class wrapping do work.

This particular functionality is required to wrap D's basic iteration
protocol, the opApply function:

http://www.digitalmars.com/d/statement.html#foreach

opApply works using a callback, as described. Because D does not (yet)
have anything like Python's "yield", wrapping it with Python's iteration
interface is turning out to be dreadfully annoying.

-Kirk McDonald
Jul 3 '06 #5
Lawrence D'Oliveiro <ld*@geek-central.gen.new_zealandwrote:
In article <44******@nntp0.pdx.net>,
Kirk McDonald <ki**************@gmail.comwrote:
I want to somehow, in some way, provide an iteration interface to this
function. Thoughts?

Run it in a separate thread/process?
Sounds best to me. Specifically, given (e.g.) something like the OP's

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)

we might have a wrapper such as:

import thread
import Queue

def wrap_cb_into_gen(func):
q = Queue.Queue(1) # maximum size of 1
def callback(item):
q.put(item)
all_done_sentinel = object()
def thread_skeleton():
func(callback)
q.put(all_done_sentinel)
thread.start_new_thread(thread_skeleton, ())
while True:
item = q.get()
if item is all_done_sentinel: break
yield item
Of course, there are lighter-weight options than a length-1 Queue for
the purpose of synchronizing these two threads, but I always tend to
prefer Queue (for its simplicity, generality, solidity) to other
synchronization structures and architectures, unless there is some very
strong reason to do otherwise in a specific case. Also, I normally use
module threading rather than the lower-level module thread -- here,
clearly, either one will do just fine.
Alex
Jul 3 '06 #6
Alex Martelli wrote:
Lawrence D'Oliveiro <ld*@geek-central.gen.new_zealandwrote:

>>In article <44******@nntp0.pdx.net>,
Kirk McDonald <ki**************@gmail.comwrote:

>>>I want to somehow, in some way, provide an iteration interface to this
function. Thoughts?

Run it in a separate thread/process?


Sounds best to me. Specifically, given (e.g.) something like the OP's

def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)

we might have a wrapper such as:

import thread
import Queue

def wrap_cb_into_gen(func):
q = Queue.Queue(1) # maximum size of 1
def callback(item):
q.put(item)
all_done_sentinel = object()
def thread_skeleton():
func(callback)
q.put(all_done_sentinel)
thread.start_new_thread(thread_skeleton, ())
while True:
item = q.get()
if item is all_done_sentinel: break
yield item
Of course, there are lighter-weight options than a length-1 Queue for
the purpose of synchronizing these two threads, but I always tend to
prefer Queue (for its simplicity, generality, solidity) to other
synchronization structures and architectures, unless there is some very
strong reason to do otherwise in a specific case. Also, I normally use
module threading rather than the lower-level module thread -- here,
clearly, either one will do just fine.
Alex
Threads are probably a weak point of mine. I have little experience with
them, and so I am inclined to paranoia while using them. What
implications does this have for "func"? In my case, it is a function in
an extension module. Does it have to be thread safe? If it isn't, what
limitations does this impose on the use of this wrapper?

All that aside, I read the previous threads but it took until just now
to understand how this works. :-) I'll probably have a go at
implementing this in D, as that will be more convenient for the library...

-Kirk McDonald
Jul 3 '06 #7
Kirk McDonald <ki**************@gmail.comwrote:
...
def func(callback):
for i in [1, 2, 3, 4, 5]:
callback(i)
...
Threads are probably a weak point of mine. I have little experience with
them, and so I am inclined to paranoia while using them. What
Paranoia is the correct state of mind to start from when threads are
involved.
implications does this have for "func"? In my case, it is a function in
an extension module. Does it have to be thread safe? If it isn't, what
limitations does this impose on the use of this wrapper?
The 'func' you gave as an example IS threadsafe (if callback is): it
does not access any global object that might be used by other threads at
the same time. If the real func *DOES* access (or, even worse, modify)
global objects -- or more generally objects that might be modified by
other threads -- then you must try to make things safe again by adding
locks (with a strong risk of deadlocking, of course).

This problem is basically (to a large extent) tied to your very specs:
you want the whole callstack up to the point where 'func' calls
'callback' to be essentially ``frozen'' while somehow the 'callback'
(the only point in which you allow intervention in the code, since you
forbid alterations of 'func'!) magically hands over the item to a yield
statement which provides the item to calling-code. Now calling code can
do anything it wants (to globals or any other objects that, for all
you've told us, 'func' might be accessing or altering too) -- and this
must not damage func's behavior.

If there are no other threads in your process, beyond the main one (on
which the generator-wrapper is called) and the specialized one which the
generator-wrapper created (to run func in), then the issues are pretty
much the same as if the generator-wrapper was able to avoid using
threads -- it's just slightly less deterministic where control may shift
between the two threads, but I believe (without being able to prove it)
that this is unlikely to matter... the key issue remains ensuring that
the *caller* of the wrapper, and func itself, don't tread on each
other's toes, regarding all that's "suspended" on the stack while the
caller does whatever it wants with the just-yielded item.

If there ARE other threads in your process, and func is not threadsafe
with respect to them, then of course you should not be calling it
(wrapped or not) without precautions in terms of locking &c -- the
wrapper doesn't matter.
All that aside, I read the previous threads but it took until just now
to understand how this works. :-) I'll probably have a go at
implementing this in D, as that will be more convenient for the library...
OK, sounds good. D looks interesting (I found Walter Bright's short
presentation of it at Google quite engaging), and I might like to give
it a try if it was as handy to interface to Python as C++ (or Java or C#
or Objective C) already are.
Alex
Jul 3 '06 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Peter Otten | last post by:
It's easy to write a function that wraps a generator and provides a callback. E. g.: import os, sys def walk(path, visit): """ Emulate os.path.walk() (simplified) using os.walk()""" for dir,...
4
by: Wai Yip Tung | last post by:
I'm attempting to turn some process than uses callback to return result into a more user friendly generator. I'm hitting some road block so any pointer would be appriciated. Let say there is an...
4
by: ma740988 | last post by:
// file sltest.h #ifndef SLTEST_H #define SLTEST_H class CallbackBase // herb shutters gotW source .. { public: virtual void operator()() const { }; virtual ~CallbackBase() = 0; };
15
by: Felix Kater | last post by:
Hi, in a given library I register callback functions with this function: bool set_callback(int index, int (*callback_function)(long)); I need the callback function to also pass the index...
8
by: kurtcobain1978 | last post by:
-------------------------------------------------------------------------------- I need to do the exactly same thing in VB.NET. Load a unmanaged C DLL dynamically and then call a function in...
0
by: Robert | last post by:
After failing on a yield/iterator-continuation problem in Python (see below) I tried the Ruby (1.8.2) language first time on that construct: The example tries to convert a block callback interface...
6
by: smmk25 | last post by:
Before I state the problem, I just want to let the readers know, I am knew to C++\CLI and interop so please forgive any newbie questions. I have a huge C library which I want to be able to use in...
5
by: Jef Driesen | last post by:
I have a C DLL that I want to use from a C# project. The C header file contains these declarations: typedef void (*callback_t) (const unsigned char *data, unsigned int size, void *userdata);...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.