5

So i have a relatively convoluted setup for something I'm working on explained as follows:

This is is python. and more of a rough outline, but it covers everything I need. Though the process next function is the same so feel free to clean that up if you want.

#timer event that runs every .1 second and processes events in a queue
some_event_timer():
    events.process_next()

class Event_queue:
    def __init__(self):
        self.events = []

    def push(self, event, parameters):
        self.events.insert(len(self.events), event, parameters)

    def process_next(self):
        event = self.pop(0)
        event[0](event[1])

class Foo:            
    def __init__(self, start_value = 1):
        self.value = start_value

    def update_value(self, multiple):
        self.value *= multiple

    def return_bah(self)
        return self.value + 3

class Bar:
    def __init__(self, number1, number2):
        self.init = number1
        self.add = number2

    def print_alt_value(self, in_value):
        print in_value * (self.init + self.add)

That is a barebones of what I have, but it illustrates my problem: Doing the below

events2 = Event_queue2()
foo1 = Foo(4)   ----> foo1.value = 4 here
bar1 = Bar(4, 2)

events2.push(foo1.update_value,1.5)
events2.push(bar1.print_alt_value,foo1.value)
events2.push(bar.print_alt_value,foo1.return_bah())

events2.process_next() ----> should process update_value to change foo.value to 6
events2.process_next() ----> should process print_alt_value in bar class - expected 36
events2.process_next() ----> should process print_alt_value - expected 54

I initially expected my output to be 36 6 * (4 + 2)

I know why its not, foo1.value and foo1.return_bah() gets passed as an evaluated parameter (correct term?). What I really want is to pass the reference to the variable or the reference to the method, rather than having it evaluate when I put it in my event queue.

Can anyone help me.

I tried searching, but I couldn't piece together what I wanted exactly. TO get what I have now I initially looked at these threads: Calling a function of a module from a string with the function's name in Python

Use a string to call function in Python

But I don't see how to support parameters from that properly or how to support passing another function or reference to a variable from those.

I suppose at least for the method call, I could perhaps pass the parameter as foo1.return.bah and evaluate in the process_next method, but I was hoping for a general way that would accept both standard variables and method calls, as the event_queue will take both.

Thank you for the help

Update edit:

So I following the suggestion below, and got really close, but:

Ok, so I followed your queue suggestion and got really close to what I want, but I don't completely understand the first part about multiple functions.

I want to be able to call a dictionary of objects with this as well. for example:

names = ["test1", "test2"]
    for name in names:
        names_objs[name] = Foo(4)

Then when attempting to push via lambda

for name in names_list:
    events2.push(lambda: names_objs[name].update_value(2))

doesn't work. When teh event actually gets processed it only runs on whatever name_objs[name] references, and if the name variable is no longer valid or has been modified outside the function, it is wrong. This actually wasn't surprising, but adding a:

name_obj_hold = name_objs[name] 

then pushing that didn't either. it again only operates on whatever name_obj_hold last referenced.

Can someone clarify the multiple funcs thing. I'm afraid I'm having trouble wrapping my head around it.

basically I need the initial method call evaluated, so something like: names_objs[name].some_func(#something in here#) gets the proper method and associated with the right class object instance, but the #something in here# doesn't get evaluated (whether it is a variable or another function) until it actually gets called from the event queue.

Community
  • 1
  • 1
Chris W
  • 77
  • 7

1 Answers1

3

Instead of passing in the function to call func1 and the arguments that should be passed to the function, pass in a function func2 that calls func1 with the arguments that should be passed in.

d = {"a":1}
def p(val):
  print val

def func1():
  p(d["a"])

def call_it(func):
  func()

call_it(func1)
d["a"] = 111
call_it(func1)

Within func1, d["a"] is not evaluated until func1 actually executes.

For your purposes, your queue would change to:

class EventQueue(object):
  def __init__(self):
    self.events = deque()
  def push(self, callable):
    self.events.append(callable)
  def process_next(self):
    self.events.popleft()()

collections.deque will be faster at popping from the front of the queue than a list.

And to use the EventQueue, you can use lambdas for quick anonymous function.

events2 = EventQueue()
foo1 = Foo(4)
bar1 = Bar(4, 2)

events2.push(lambda: foo1.update_value(1.5))
events2.push(lambda: bar1.print_alt_value(foo1.value))
events2.push(lambda: bar1.print_alt_value(foo1.return_bah()))

events2.process_next()
events2.process_next() # 36.0
events2.process_next() # 54.0

For Edit:

In this case you need to "capture" the value in a variable that is more tightly scoped than the loop. You can use a normal function and partial() to achieve this.

for name in names_list:
  def update(name):
    names_objs[name].update_value(2)
  events2.push(partial(update, name))
unholysampler
  • 16,139
  • 5
  • 44
  • 63
  • I clarified some in my original question if you could kindly help clarify, or perhaps let me know if what I want is unreasonable. Thanks. – Chris W Nov 19 '13 at 23:59