Access variable from Class in main - python

I'm trying to learn Python and I'm stuck with shared variables between classes.
I have a class that sets a variable like this:
class CheckStuff(threading.Thread):
def __init__(self, debug):
threading.Thread.__init__(self)
self.debug = debug
self.newGames = False
def get_new_games(self):
return self.newGames
def set_new_games(self, new_games):
self.newGames = new_games
def run(self):
# DO STUFF #
# ... #
self.set_new_games(True)
return
I would like to access new_games from my main, I tried like this:
if __name__ == "__main__":
debug = True
t1 = cs.CheckStuff(debug)
t1.start()
t1.join()
print(cs.CheckStuff(debug).get_new_games())
exit()
But this returns always False. Where am I wrong? Any hints appreciated

You're creating a new instance of CheckStuff, which hasn't done anything so its newGames attribute is False. The instance that did run is t1, you should use that:
print(t1.get_new_games())
(Note, there doesn't really seem to be a good reason to use threads here, but never mind.)

In the following line
print(cs.CheckStuff(debug).get_new_games())
you make a new instance of CheckStuff class, and in the constructor you set self.newGames = False. Then, by calling get_new_game() method you ask the object to return its newGame attribute, which is obviously False.

Related

Python multiprocessing.Pool.apply_async() not executing class function

In a custom class I have the following code:
class CustomClass():
triggerQueue: multiprocessing.Queue
def __init__(self):
self.triggerQueue = multiprocessing.Queue()
def poolFunc(queueString):
print(queueString)
def listenerFunc(self):
pool = multiprocessing.Pool(5)
while True:
try:
queueString = self.triggerQueue.get_nowait()
pool.apply_async(func=self.poolFunc, args=(queueString,))
except queue.Empty:
break
What I intend to do is:
add a trigger to the queue (not implemented in this snippet) -> works as intended
run an endless loop within the listenerFunc that reads all triggers from the queue (if any are found) -> works as intended
pass trigger to poolFunc which is to be executed asynchronosly -> not working
It works as soon as I source my poolFun() outside of the class like
def poolFunc(queueString):
print(queueString)
class CustomClass():
[...]
But why is that so? Do I have to pass the self argument somehow? Is it impossible to perform it this way in general?
Thank you for any hint!
There are several problems going on here.
Your instance method, poolFunc, is missing a self parameter.
You are never properly terminating the Pool. You should take advantage of the fact that a multiprocessing.Pool object is a context manager.
You're calling apply_async, but you're never waiting for the results. Read the documentation: you need to call the get method on the AsyncResult object to receive the result; if you don't do this before your program exits your poolFunc function may never run.
By making the Queue object part of your class, you won't be able to pass instance methods to workers.
We can fix all of the above like this:
import multiprocessing
import queue
triggerQueue = multiprocessing.Queue()
class CustomClass:
def poolFunc(self, queueString):
print(queueString)
def listenerFunc(self):
results = []
with multiprocessing.Pool(5) as pool:
while True:
try:
queueString = triggerQueue.get_nowait()
results.append(pool.apply_async(self.poolFunc, (queueString,)))
except queue.Empty:
break
for res in results:
print(res.get())
c = CustomClass()
for i in range(10):
triggerQueue.put(f"testval{i}")
c.listenerFunc()
You can, as you mention, also replace your instance method with a static method, in which case we can keep triggerQueue as part of the class:
import multiprocessing
import queue
class CustomClass:
def __init__(self):
self.triggerQueue = multiprocessing.Queue()
#staticmethod
def poolFunc(queueString):
print(queueString)
def listenerFunc(self):
results = []
with multiprocessing.Pool(5) as pool:
while True:
try:
queueString = self.triggerQueue.get_nowait()
results.append(pool.apply_async(self.poolFunc, (queueString,)))
except queue.Empty:
break
for r in results:
print(r.get())
c = CustomClass()
for i in range(10):
c.triggerQueue.put(f"testval{i}")
c.listenerFunc()
But we still need to reap the pool_async results.
Okay, I found an answer and a workaround:
the answer is based the anser of noxdafox to this question.
Instance methods cannot be serialized that easily. What the Pickle protocol does when serialising a function is simply turning it into a string.
For a child process would be quite hard to find the right object your instance method is referring to due to separate process address spaces.
A functioning workaround is to declare the poolFunc() as static function like
#staticmethod
def poolFunc(queueString):
print(queueString)

How to access a variable outside of a class function if it wasn't passed to the function

I have been tasked to modify the behavior of a function in one of our Python classes.
Our function takes a few parameters, one being a debug flag. Currently the if the debug flag is not specified then we assume it to be False. What we need it to do is when debug is not specified, check the variable "debug" from the calling code and use that value, if it exists.
I would simply change the name of the debug parameter in the function declaration, except that we have a lot of legacy code that uses that flag.
This is in Jupyter Lab, if it makes any difference.
Sample code:
class MyClass:
#classmethod
def fn(self, debug=None):
if debug is None:
try:
debug = parent.debug
except Exception as e:
print(e)
debug = "BAD"
return debug
debug = True
x = myClass
print( x.fn() )
I would want the output to be "True" but it ends up being:
global name 'parent' is not defined
BAD
Is what I am trying to do possible? If so, how?
Use globals()['debug'] instead.
Or replace your fn() method to:
#classmethod
def fn(self, debug=None):
if debug is None:
debug = globals().get('debug', 'BAD')
return debug
You access variable parent which us not defined in your code.
Firstly, the MyClass in your code isn't inherited from any other class. Technically, the class doesn't have any parent(except the obj class by default), so it's impossible to access something that doesn't exist like 'parent.debug'. Secondly, as for your problem, I'm assuming that you want to access the debug variable that is initialized outside the class then you can easily do that by doing something like this:
global debug
class MyClass:
#classmethod
def fn(self, debug=None):
if debug is None:
try:
debug = debugOut
except Exception as e:
print(e)
debug = "BAD"
return debug
debugOut = True
x = MyClass()
print(x.fn())
The above code returns True

Modify a class value (self.var), that is specified as a parameter to a function

I'm trying to create reusable code that is modifying a class variable, whose name is not known to the method that is doing the logic.
In Example.receive_button_press, I am trying to call a function on an object that I have passed into the method, whose variable(s) would also be provided as parameters.
Can this be done in python? The code below does not quite work, but illustrates what I am trying to achieve.
import tkinter as tk
from tkinter import filedialog
class SomeOtherClass():
_locked_button = False
def __init__(self):
pass
def do_button_press(self, button):
if button:
return
button = True
someVal = tk.filedialog.askopenfilename(initialdir='\\')
button = False
return someVal
class Example():
def __init__(self):
pass
def receive_button_press(self, obj, func, var):
return obj.func(var)
if __name__ == "__main__":
root = tk.Tk()
toyClass = Example()
other = SomeOtherClass()
myButton = tk.Button(text="Press",
command=toyClass.receive_button_press(obj = other,func = other.do_button_press,
var=SomeOtherClass._locked_button))
myButton.pack()
root.mainloop()
Callable Objects
You need to understand how callables work in python. Specifically, callable objects are regular objects. The only special thing about them is that you can apply the () operator to them. Functions, methods and lambdas are just three of many types of callables.
Here is a callable named x:
x
x might have been defined as
def x():
return 3
Here is the result of calling x, also known as the "return value":
x()
Hopefully you can see the difference. You can assign x or x() to some other name, like a. If you do a = x, you can later do a() to actually call the object. If you do a = x(), a just refers to the number 3.
The command passed to tk.Button should be a callable object, not the result of calling a function. The following is the result of a function call, since you already applied () to the name receive_button_press:
toyClass.receive_button_press(obj=other, func=other.do_button_press,
var=SomeOtherClass._locked_button)
If this function call were to return another callable, you could use it as an argument to command. Otherwise, you will need to make a callable that performs the function call with no arguments:
lambda: toyClass.receive_button_press(obj=other, func=other.do_button_press,
var=SomeOtherClass._locked_button)
As an aside, if you add a __call__ method to a class, all if its instances will be callable objects.
Bound Methods
The object other.do_button_press is called a bound method, which is a special type of callable object. When you use the dot operator (.) on an instance (other) to get a function that belongs to a class (do_button_press), the method gets "bound" to the instance. The result is a callable that does not require self to be passed in. In fact, it has a __self__ attribute that encodes other.
Notice that you call
other.do_button_press(button)
not
other.do_button_press(other, button)
That's why you should change Example to read
def receive_button_press(self, func, var):
return func(var)
References to Attributes
You can access attributes in an object in a couple of different ways, but the simplest is by name. You can access a named attribute using the builtin hasattr and set them with setattr. To do so, you would have to change do_button_press to accept an object and an attribute name:
def do_button_press(self, lock_obj, lock_var):
if getattr(lock_obj, lock_var, False):
return
setattr(lock_obj, lock_var, True)
someVal = tk.filedialog.askopenfilename(initialdir='\\')
setattr(lock_obj, lock_var, False)
return someVal
Interfaces and Mixins
If this seems like a terrible way to do it, you're right. A much better way would be to use a pre-determined interface. In python, you don't make an interface: you just document it. So you could have do_button_press expect an object with an attibute called "locked" as "button", rather than just a reference to an immutable variable:
class SomeOtherClass():
locked = False
def do_button_press(self, lock):
if lock.locked:
return
lock.locked = True
someVal = tk.filedialog.askopenfilename(initialdir='\\')
lock.locked = False
return someVal
At the same time, you can make a "mixin class" to provide a reference implementation that users can just stick into their base class list. Something like this:
class LockMixin:
locked = False
class SomeOtherClass(LockMixin):
def do_button_press(self, lock):
if lock.locked:
return
lock.locked = True
someVal = tk.filedialog.askopenfilename(initialdir='\\')
lock.locked = False
return someVal
Locks
Notice that I started throwing the term "Lock" around a lot. That's because the idea you have implemented is called "locking" a segment of code. In python, you have access to thread locks via threading.Lock. Rather than manually setting locked, you use the acquire and release methods.
class SomeOtherClass:
def do_button_press(self, lock):
if lock.acquire(blocking=False):
try:
someVal = tk.filedialog.askopenfilename(initialdir='\\')
return someVal
finally:
lock.release()
Alternatively, you can use the lock as a context manager. The only catch is that acquire will be called with blocking=True by default, which may not be what you want:
def do_button_press(self, lock):
with lock:
someVal = tk.filedialog.askopenfilename(initialdir='\\')
return someVal
Decorators
Finally, there is one more tool that may apply here. Python lets you apply decorators to functions and classes. A decorator is a callable that accepts a function or class as an argument and returns a replacement. Examples of decorators include staticmethod, classmethod and property. Many decorators return the original function more-or-less untouched. You can write a decorator that acquires a lock and releases it when you're done:
from functools import wraps
from threading import Lock
def locking(func):
lock = Lock()
#wraps(func)
def wrapper(*args, **kwargs):
if lock.acquire(blocking=False):
try:
return func(*args, **kwargs)
except:
lock.release()
return wrapper
Notice that the wrapper function is itself decorated (to forward the name and other attributes of the original func to it). It passes through all the input arguments and return of the original, but inside a lock.
You would use this if you did not care where the lock came from, which is likely what you want here:
class SomeOtherClass:
#locking
def do_button_press(self):
return tk.filedialog.askopenfilename(initialdir='\\')
Conclusion
So putting all of this together, here is how I would rewrite your toy example:
import tkinter as tk
from tkinter import filedialog
from functools import wraps
from threading import Lock
def locking(func):
lock = Lock()
#wraps(func)
def wrapper(*args, **kwargs):
if lock.acquire(blocking=False):
try:
return func(*args, **kwargs)
finally:
lock.release()
return wrapper
class SomeOtherClass():
#locking
def do_button_press(self, button):
return tk.filedialog.askopenfilename(initialdir='\\')
if __name__ == "__main__":
root = tk.Tk()
toyClass = SomeOtherClass()
myButton = tk.Button(text="Press", command=toyClass.do_button_press)
myButton.pack()
root.mainloop()

How to access instance variables from a threaded instance method?

I have been struggling to figure out how to access the the instance variable from an instance method running in a separate thread like in the example below. I thought the loop would be skipped after the test.set_finished method is called from the main thread after 5 seconds.
However my loop keeps counting and I fail to understand why. Would be nice if someone could help me out a little and tell me what I need to do differently or what I'm trying to do is not possible at all.
import time
import concurrent.futures
class TestClass(object):
def __init__(self):
super(TestClass, self).__init__()
self.finished = False
def do_something(self):
i = 0
while not self.finished:
i+=1
print(i)
time.sleep(1)
def set_finished(self, finished_arg):
self.finished = finished_arg
def startThreadedMethod(self):
with concurrent.futures.ThreadPoolExecutor() as executor:
t1 = executor.submit(self.do_something)
test = TestClass()
test.startThreadedMethod()
time.sleep(5)
test.set_finished(True)

Python: Passing a class member function to another class's callback

Can I pass class A into class B so that B can run a callback using A's member function?
I am trying to write a Python leg class for a robot I am building. I am using a Raspberry Pi as the main computer, and Martin O'Hanlon's KY040 rotary encoder library KY040 to detect each 1/4 rotation of the leg. To this end, I watch for the first of several clicks, sleep for a short time, stop the servo, and now a 1/4 rotation has been achieved. In standalone, unthreaded code this works fine, but creating a class has been a challenge.
Details:
A threaded sentinel loop watches a boolean (quarterTurn) to signal that a rotation must be carried out.
def run(self):
print "leg running"
while self._running:
sleep(.0001)
if self.quarterTurn:
print "quarterTurn is: " + str(self.quarterTurn)
self.qTurn(self.quarterCount)
qTurn accesses a pwm controller to activate the motors, and reset quarterTurn to false.
def qTurn(self, quarters):
count = 0
while count < quarters:
sleep(.0001)
self.setMotor(self.maxPulse)
if self.ClickedOnce:
count = count + 1
sleep(.17)
self.parkMotor()
sleep(.04)
self.clickedOnce = False
self.quarterTurn = False
The trick is that O'Hanlon's class is already threaded. On one hand, it is convenient, on the other, it makes my class more complex. The KY040 makes use of a callback function to provide feedback, but using this within my class is the source of my trouble.
I need the callback to modify a a boolean in my leg class, but this function is only called by the KY040 class, which tries to pass itself into the function.
def rotaryChange(self, pin):
self.clickedOnce = True
Since the code is open source (thank you, O'Hanlon), I thought I could modify the constructor of the KY040 to let me pass my leg class into it, so that I could modify the correct data.
O'Hanlon's Original Constructor:
def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None,rotaryBouncetime=250, switchBouncetime=300):
# persist values
self.clockPin = clockPin
self.dataPin = dataPin
self.switchPin = switchPin
self.rotaryCallback = rotaryCallback
self.switchCallback = switchCallback
self.rotaryBouncetime = rotaryBouncetime
self.switchBouncetime = switchBouncetime
#setup pins
GPIO.setup(clockPin, GPIO.IN)
GPIO.setup(dataPin, GPIO.IN)
if None != self.switchPin:
GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
I added a "host" variable, into which I pass the leg class:
def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None, host=None, rotaryBouncetime=250, switchBouncetime=300):
# persist values
self.clockPin = clockPin
self.dataPin = dataPin
self.switchPin = switchPin
self.rotaryCallback = rotaryCallback
self.switchCallback = switchCallback
self.rotaryBouncetime = rotaryBouncetime
self.switchBouncetime = switchBouncetime
# My Change
self.host = host
#setup pins
GPIO.setup(clockPin, GPIO.IN)
GPIO.setup(dataPin, GPIO.IN)
if None != self.switchPin:
GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
The modified constructor would be called like so:
self.encoder = KY040(self.clockPin, self.dataPin, rotaryCallback=self.rotaryChange, host=self)
O'Hanlon's callback now passes the host along:
def _clockCallback(self, pin):
# My change
self.rotaryCallback(pin, self.host)
My new callback:
def rotaryChange(pin, host):
host.clickedOnce = True
Unfortunately, after making sure the modified code is installed with the setup script, it doesn't seem to acknowledge my new additions. I run my program and receive the follwing error:
Traceback (most recent call last):
File "ctf.py", line 18, in <module>
LR = leg.leg(lr_chan, lr_max, lr_park, lr_clk, lr_data);
File "/home/[user]/hexacrescentapod/leg.py", line 47, in __init__
self.encoder = KY040(self.clockPin, self.dataPin,
rotaryCallback=self.rotaryChange, host=self)
TypeError: __init__() got an unexpected keyword argument 'host'
This is a little confusing because of your wording. Are you actually trying to pass a class in as you say, or an instance of that class as you seem to be doing? Which class is rotaryChange defined in?
Anyway, it looks like what you're actually trying to do is pass self.rotaryChange as a callback.
This already works, without any changes. self.rotaryChange is a bound method, meaning it knows what that self was when it was created, and will pass it when it's called. This may be easier to see with an example:
>>> class Spam:
... def eggs(self):
... pass
>>> spam = Spam()
>>> spam
<__main__.Spam at 0x119947630>
>>> spam.eggs
<bound method Spam.eggs of <__main__.Spam object at 0x119947630>>
Notice that it's a bound method of the spam object. When you call spam.eggs(), that spam object will be passed as the self argument.
This means you don't need to pass a host in, because it's already available as self. And, since that's the only thing you do with host, you don't need to pass around host in the first place. Which means you can revert all of your changes to the library code.
You do need to define your callback method as a proper method, with self as the first argument. But that's it. Then you can just pass rotaryCallback=self.rotaryChange to the constructor, and everything will work.
At a first look, it looks like your new callback is missing a self field?
The original function was
def rotaryChange(self, pin):
self.clickedOnce = True
But your implementation is:
def rotaryChange(pin, host):
host.clickedOnce = True
If this function sits inside a class it needs to have a self parameter

Categories