7

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.

1 Answer 1

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))
Sign up to request clarification or add additional context in comments.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.