Passing objects around an event queue in Python - python

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.

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

Related

Alternating the use of classes/globals with closures in Python

I came across closures in python, and I've been tinkering around the subject.
Please Correct me if I'm wrong here, but what I understood for when to use closures (generally) is that it can be used as a replacement of small classes (q1) and to avoid the use of globals (q2).
Q1: [replacing classes]
Any instance created from the datafactory class will have it's own list of data, and hence every appending to that object's list will result in an incremental behavior. I understand the output from an OO POV.
class datafactory():
def __init__(self):
self.data = []
def __call__(self, val):
self.data.append(val)
_sum = sum(self.data)
return _sum
incrementwith = datafactory()
print(incrementwith(1))
print(incrementwith(1))
print(incrementwith(2))
OUTPUT:
1
2
4
I tried replacing this with a closure, it did the trick, but my understanding to why/how this is happening is a bit vague.
def data_factory():
data = []
def increment(val):
data.append(val)
_sum = sum(data)
return _sum
return increment
increment_with = data_factory()
print(increment_with(1))
print(increment_with(1))
print(increment_with(2))
OUTPUT:
1
2
4
What I'm getting is that the data_factory returns the function definition of the nested increment function with the data variable sent along as well, I would've understood the output if it was something like this:
1
1
2
But how exactly the data list persists with every call?
Shouldn't variables defined in a function die after the function finishes execution and get regenerated and cleared out with the next fn call?
Note: I know that this behavior exists normally in a function defined with default parameters like def func(val, l = []): where the list will not be cleared on every fn call, but rather be updated with a new element/append, which is also something that I do not fully understand.
I would really appreciate an academic explanation to what happens in both scenarios (OO and closures).
Q2: [replacing use of global]
Is there a way using closures to increment the following variable without using globals or a return statement ?
a = 0
print("Before:", a) # Before: 0
def inc(a):
a += 1
print("After:", a) # After: 0
Thank you for your time.
For the first question, I found after some digging that passing mutables as default parameters isn't really a good move to make:
https://florimond.dev/blog/articles/2018/08/python-mutable-defaults-are-the-source-of-all-evil/#:~:text=of%20this%20mess.-,The%20problem,or%20even%20a%20class%20instance.

pass return of a function into another

I have a question / problem and I don't know how to solve it. Suppose you have three functions, function 1, function 2 and function 3. In function 1 you do some operations and you give a specific return which will be used as input for the second function. In the second function you do some specific calculation and also finish with return, which you pass in a third function
My problem is that as soon as I pass the return of function 1 into function 2 all the calculation in function 1 is repeated (calculation is here in this case, several plots) The same goes for function two into three, now I get results from function 1 and function 2. I hope you do understand what I mean.
What I want is just the return value of func 1 for func 2 and return value of func 2 for three and not the entire function body.
Here is what my code looks like:
class test:
def __(self)__:
self.attribute1=pd.read_csv(...)
self.attribite2=pd.read_csv(...)
def func1(self):
plt.plot(a,b)
plt.plot(c,d)
return x
def func2(self):
self.data_2=self.func1()
plt.plot(e,f)
plt.plot(g,h)
return y
def func3(self):
self.data_3=self.func2()
plt.plot(i,j)
data_test=test()
print(data_test.func2())
My problem is that (let's focus on func2). If I use the input from func1 and execute my code for func2 I get also the two plots. I dont want to have that. I just want to see the plots(e,f) and plots(g,h) instead of plots(a,b), plots(c,d), plots(e,f) and plots(g,h)
Your class definition do not follow the OOP clean design, for which every method should performa the most atomic task possible.
Your methods func1, func2 and func3, they all do at least 2 tasks: plot something and return something else.
Consider changing your class so every method do one and only one thing, defining public APIS and private methods, for instance:
class test:
def __(self)__:
self.attribute1 = []
self.attribite2 = []
def _func1(self):
return x
def _func2(self):
self.data_2 = self._func1()
return y
def _func3(self):
self.data_3 = self._func2()
def func2(self):
self._func2()
plt.plot(e,f)
plt.plot(g,h)
def func3(self):
self._func3()
plt.plot(e,f)
plt.plot(g,h)
data_test=test()
data_test.func2()
This way func2 and func3 are public apis (aka: intended to be called from outside the class) that will "do the work" (setting stuff in self.data_2 and self.data_3) AND plot; while _func2 and _func3 are private methods (aka, methods that only the class itself is supposed to use) will only do the work.
Now, calling func2 will use methods _func1 and _func2, but only plot what's defined in func2.
You say that you "pass the return of function 1 into function 2", but you never do that.
You're not passing any results anywhere, and none of your functions take any input (except self) - you're calling the functions directly in each one.
In other words, every time you call function2, it calls function1, and every time you call function3, it calls function2, which in turn calls function1.
Code that matches your description would look like this:
class test:
def __(self)__:
self.attribute1=pd.read_csv(...)
self.attribite2=pd.read_csv(...)
def func1(self):
plt.plot(a,b)
plt.plot(c,d)
return x
def func2(self, a):
self.data_2 = a
plt.plot(e,f)
plt.plot(g,h)
return y
def func3(self, x):
self.data_3 = x
plt.plot(i,j)
return z
data_test = test()
print(data_test.func3(data_test.func2(data_test.func1())))

How to create variables local to with statement?

I would like to create temporary variables visible in a limited scope.
It seems likely to me that you can do this with a "with" statement, and I would think there is a construct that makes it easy to do, but I cannot seem to find it.
I would like something like the following (but it does not work this way of course):
pronunciation = "E_0 g z #_1 m p l"
# ...
with pronunciation.split() as phonemes:
if len(phonemes) > 2 or phonemes[0].startswith('E'):
condition = 1
elif len(phonemes) < 3 and phonemes[-1] == '9r':
condition = 2
So is there a simple way to make this work, using built-ins?
Thanks!
Python creates local variables with function scope (once a name is used it stays alive until the end of the function).
If you really want to limit scope then "del <var>" when you want it explicitly discarded, or create separate function to act as a container for a more limited scope.
You can create a method
def process_pronunciation(pronunciation):
phonemes = pronunciation.split()
if len(phonemes) > 2 or phonemes[0].startswith('E'):
condition = 1
elif len(phonemes) < 3 and phonemes[-1] == '9r':
condition = 2
return condition
When you call the method, the local variable phonemes won't be available in the global namespace.
pronunciation = "E_0 g z #_1 m p l"
condition = process_phonemes(pronunciation)
You could do it with with, but I don't think it's worth the trouble. Basically (in a python function) you have two scopes - global or local, that's it. If you want a symbol to have a lifespan shorter than the function you'll have to delete it afterwards using del. You could define your own context manager to make this happen:
class TempVar:
def __init__(self, loc, name, val):
self.loc = loc
self.name = name
self.val
def __enter__(self):
if self.name in self.loc:
self.old = self.loc[self.name]
self.loc[self.name] = self.val
def __exit__(self, *exc):
if hasattr(self, "old"):
self.loc[self.name] = self.old
else:
del self.loc[self.name]
then you can use it to get a temporary variable:
with TempVar(locals(), "tempVar", 42):
print(tempVar)
The working is that it modifies the dict containing local variables (which is supplied to the constructor via locals()) on entry and restoring it when leaving. Please note that this relies on that modifying the result returned by locals() actually modifies the local namespace - the specification does NOT guarantee this behaviour.
Another (and safer) alternative that was pointed out is that you could define a separate function which would have it's own scope. Remember it's perfectly legal to nest functions. For example:
def outer():
def inner(tempVar):
# here tempVar is in scope
print(tempVar)
inner(tempVar = 42)
# here tempVar is out of scope
with statement does not have its own scope , it uses the surrounding scope (like if the with statement is directly inside the script , and not within any function, it uses global namespace , if the with statement is used inside a function, it uses the function's namespace(scope)).
If you want the statements inside a with block to run in its own local scope, one possible way would be to move the logic to a function , that way the logic would be running in its own scope (and not the surrounding scope of with.
Example -
def function_for_with(f):
#Do something.
with pronunciation.split() as phonemes:
function_for_with(phonemes)
Please note, the above will not stop phonemes from being defined in the surrounding scope.
If you want that as well (move the phonemes into its own scope), you can move the complete with statement inside a function. Example -
def function_with(pronunciation):
with pronunciation.split() as phonemes:
#do stuff
pronunciation = "E_0 g z #_1 m p l"
function_with(pronunciation)
Expanding on #skyking's answer, here's an even more magical implementation of the same idea that reads almost exactly like you wrote. Introducing: the with var statement!1
class var:
def __init__(self, value):
import inspect
self.scope = inspect.currentframe().f_back.f_locals
self.old_vars = set(self.scope.keys())
self.value = value
def __enter__(self):
return self.value
def __exit__(self, type, value, traceback):
for name in set(self.scope.keys()) - self.old_vars:
del self.scope[name]
### Usage:
line = 'a b c'
with var (line.split()) as words:
# Prints "['a', 'b', 'c']"
print(words)
# Causes a NameError
print(words)
It does all the nasty extracting of local variables and names for you! How swell. If you space it quirkily like I did and hide the definition in a from boring_stuff import * statement, you can even pretend var is a keyword to all of your confused co-workers.
[1] If you actually use this, the ghost of a dead parrot will probably haunt you forever. The other answers provide much saner solutions; this one is more of a joke.

__init__ with function as parameter (using the NetworkX)

The Question
I want to be able to initialize an object with a function that references the instance's attributes. What I want I tried to capture in this snippet, which produces a NameError: "global name 'self' is not defined":
class Test(object):
def __init__(self, function = None):
self.dicty = {1:{'height': 4, 'width': 2}, 2:{'height': 1, 'width': 2} }
if function == None:
self.function = lambda x : self.dicty[x]['height']
else:
self.function = function
if __name__ == '__main__':
def func1(x):
return self.dicty[x]['width']
def func2(x):
return self.dicty[x]['width']**2
G = Test(function = func1)
H = Test(function = func2)
I could solve the problem by creating a bunch of subclasses to Test, but that doesn't seem readable.
The Motivation
I am using NetworkX to do Python modeling and experiments. I was looking at the classic Albert-Barabasi Model and creating subclasses of the DiGraph class that included a Preference(self, node), Attachment(self, parent, child), and then a Grow(self, max_allowable_nodes). Instead of creating a whole bunch of subclasses like I mentioned before, I would love to be able to create an instance that modifies preference(). This would allow me to run numerical experiments without my code looking too much like Frankenstein. Looking forward to learning something new.
Edit:
Didn't know about the types class or the general idea of reflection. Obviously, still pretty new here. Really appreciate everyone answering my questions and pointing me in the right direction!
Given that the lambda you create in your __init__ refers to the instance (self), it looks like you want to attach a method to your instance, whereas here you're attaching a function. You need to create a method from the function and attach it to the instance:
import types
class Test(object):
def __init__(self, function = None):
self.dicty = {1:{'height': 4, 'width': 2}, 2:{'height': 1, 'width': 2} }
if function == None:
function = lambda self, x: self.dicty[x]['height']
self.function = types.MethodType(function, self)
A method is basically a function that is always passed the instance as the first argument, so you need to ensure any function you pass into your initialiser has self as the initial argument.
>>> t1 = Test()
>>> t1.function(1)
4
>>> t2 = Test(lambda self, x: self.dicty[x]['width'])
>>> t2.function(1)
2
When you define func1, there is no such thing as self. It's not an argument to the function, and it's not in any higher scope.
You could, instead, define a function that takes the dict you use as an argument and operates on that. In the Test class, you can then call the function on self.dicty. This would require you to change your lambda to also take dicty and x instead of just x.
def func1(dicty, x):
return dicty[x]['width']
...and in Test...
class Test(object):
# ... current code but with lambda tweak:
# lambda dicty, x: dicty[x]['height']
def do_something(self, x):
self.function(self.dicty, x)
Without seeing the rest of your code, it's hard to know what further simplifications you could make. But since all the functions seem to be using dicty[x] anyway, you could just write them to take that directly.

Python OOP: functions from a dictionary do not affect class instance variables

I am new to Python but write programs for a hobby, so I have moderate knowledge of OOP and computer programming in general. I have started working on an simple animal simulator. In what might very well be a heathen move, I am trying to store all of the 'action functions' of the animal in a dictionary, so that each function is accessible by a string. For example, dict['SLEEP']() calls the sleep function.
I could find no examples of what I am trying to accomplish, and frankly am not sure how to intelligently describe my problem. See the bare-bones code below:
class Animal:
def __init__(self):
self.health = 100
self.actions = {} # dictionary of functions
self.initializeAnimal()
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
def initializeSleep(self):
RESTORED_HEALTH = 20
# other constants
def sleep(self):
self.health += RESTORED_HEALTH
# utilize other constants
return sleep
Then, the animal handler would perform something along the following lines:
for animal in animalList:
animal.actions['SLEEP']()
I'd of course like the animal's health to increase by 20 when the sleep function is called. Instead, nothing happens. After some research and experimenting, I see that the self passed to the sleep() function apparently refers to initializeSleep() rather than the animal class.
I am at somewhat of a loss as to how I would change the health of the animal when calling functions in this manner. Do I have to somehow make use of super-class calls?
edit: clarify syntax
Python does some maneuvers so that functions defined in a class body actually behave as "methods" - and thus, get the "self" parameter added authomatically.
It is not hard to understand how that is done - and to emulate it for an explicit dictionary as you plan - but first, consider that you can retrieve a method name using a string, without resorting to storing them in dictionaries as you plan - you can simply do:
class Animal(object):
...
def sleep(self, ...):
...
my_cow = Animal()
function = getattr(my_cow, "sleep")
function ( )
# and of course, the two preceeding lines can be in a single expression:
getattr(a, "sleep")()
Now, let's see for the dicionary -
since you defien the actual "sleep" function as a nested function, it will "see" the "self" variable as it exists in the invocation of initializeSleep() - which means what you are doing should just work - as soons as you fix the call to initializeSleep() by prefixing it with the self. , as in:
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
And remove the "self" parameter from the actual "sleep" function - it does not need it,
as it will "see" the nonlocal self variable in the enclosing scope:
def initializeSleep(self):
RESTORED_HEALTH = 20
# other constants
def sleep():
self.health = RESTORED_HEALTH
# utilize other constants
return sleep
(The other constants defined inside the initializeSLeep will also be visible inside sleep as nonlocal variables)
You don't need to put the self attribute into the sleep function.
Its perfectly valid to do the following:
class Animal:
def __init__(self):
self.health = 100
self.actions = {} # dictionary of functions
self.initializeAnimal()
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
def initializeSleep(self):
RESTORED_HEALTH += 20
# other constants
def sleep():
self.health += RESTORED_HEALTH
# utilize other constants
return sleep
a = Animal()
print(a.health)
a.actions['SLEEP']()
print(a.health)
Output:
100
120
As stated, you forogt the += in self.health = RESTORED_HEALTH.
You also missed the self in self.initializeSleep()

Categories

Resources