I'm going through a Python OOPs book by Dusty Phillips. I fail to understand a particular program in the book, chapter 7 - Python Object-oriented Shortcuts. The extended version of the code is available here
Although the program comes under the topic Functions are objects too, the provided program also uses a strange code, which i feel, more of imply the opposite (using objects as functions).
I have pointed out the line in the code where i have the doubt. How is that variable callback of TimedEvent used like a function Timer class ? What is going on here in this part.
import datetime
import time
class TimedEvent:
def __init__(self, endtime, callback):
self.endtime = endtime
self.callback = callback
def ready(self):
return self.endtime <= datetime.datetime.now()
class Timer:
def __init__(self):
self.events = []
def call_after(self, delay, callback):
end_time = datetime.datetime.now() + \
datetime.timedelta(seconds=delay)
self.events.append(TimedEvent(end_time, callback))
def run(self):
while True:
ready_events = (e for e in self.events if e.ready())
for event in ready_events:
event.callback(self) ----------------> Doubt
self.events.remove(event)
time.sleep(0.5)
Both are true
functions are objects: do a dir(f) on a function to view its attributes
objects can be used as functions: just add __call__(self, ...) method and use the object like a function.
In general things that can be called using a syntax like whatever(x, y, z) are called callables.
What the example is trying to show is that methods are just object attributes that are also callables. Just like you can write obj.x = 5, you can also write obj.f = some_function.
Yes, that example indeed shows that functions are object. You can assign an object to the callback attribute and this tries to show that the object you assign to callback can be a function.
class TimedEvent:
def __init__(self, endtime, callback):
self.endtime = endtime
self.callback = callback
What is missing to make it clear is the initialization. You could, for example, do this:
def print_current_time():
print(datetime.datetime.now().isoformat())
event = TimedEvent(endtime, print_current_time)
event.callback()
This will actually call print_current_time, because event.callback is print_current_time.
Related
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()
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
I am trying to build some UI panels for an Eclipse based tool. The API for the tool has a mechanism for event handling based on decorators, so for example, the following ties callbackOpen to the opening of a_panel_object:
#panelOpenHandler(a_panel_object)
def callbackOpen(event):
print "opening HERE!!"
This works fine, but I wanted to wrap all of my event handlers and actual data processing for the panel behind a class. Ideally I would like to do something like:
class Test(object):
def __init__(self):
# initialise some data here
#panelOpenHandler(a_panel_object)
def callbackOpen(self, event):
print "opening HERE!!"
But this doesn't work, I think probably because I am giving it a callback that takes both self and event, when the decorator is only supplying event when it calls the function internally (note: I have no access to source code on panelOpenHandler, and it is not very well documented...also, any error messages are getting swallowed by Eclipse / jython somewhere).
Is there any way that I can use a library decorator that provides one argument to the function being decorated on a function that takes more than one argument? Can I use lambdas in some way to bind the self argument and make it implicit?
I've tried to incorporate some variation of the approaches here and here, but I don't think that it's quite the same problem.
Your decorator apparently registers a function to be called later. As such, it's completely inappropriate for use on a class method, since it will have no idea of which instance of the class to invoke the method on.
The only way you'd be able to do this would be to manually register a bound method from a particular class instance - this cannot be done using the decorator syntax. For example, put this somewhere after the definition of your class:
panelOpenHandler(context.controls.PerformanceTuneDemoPanel)(Test().callbackOpen)
I found a work around for this problem. I'm not sure if there is a more elegant solution, but basically the problem boiled down to having to expose a callback function to global() scope, and then decorate it with the API decorator using f()(g) syntax.
Therefore, I wrote a base class (CallbackRegisterer), which offers the bindHandler() method to any derived classes - this method wraps a function and gives it a unique id per instance of CallbackRegisterer (I am opening a number of UI Panels at the same time):
class CallbackRegisterer(object):
__count = 0
#classmethod
def _instanceCounter(cls):
CallbackRegisterer.__count += 1
return CallbackRegisterer.__count
def __init__(self):
"""
Constructor
#param eq_instance 0=playback 1=record 2=sidetone.
"""
self._id = self._instanceCounter()
print "instantiating #%d instance of %s" % (self._id, self._getClassName())
def bindHandler(self, ui_element, callback, callback_args = [], handler_type = None,
initialize = False, forward_event_args = False, handler_id = None):
proxy = lambda *args: self._handlerProxy(callback, args, callback_args, forward_event_args)
handler_name = callback.__name__ + "_" + str(self._id)
if handler_id is not None:
handler_name += "_" + str(handler_id)
globals()[handler_name] = proxy
# print "handler_name: %s" % handler_name
handler_type(ui_element)(proxy)
if initialize:
proxy()
def _handlerProxy(self, callback, event_args, callback_args, forward_event_args):
try:
if forward_event_args:
new_args = [x for x in event_args]
new_args.extend(callback_args)
callback(*new_args)
else:
callback(*callback_args)
except:
print "exception in callback???"
self.log.exception('In event callback')
raise
def _getClassName(self):
return self.__class__.__name__
I can then derive a class from this and pass in my callback, which will be correctly decorated using the API decorator:
class Panel(CallbackRegisterer):
def __init__(self):
super(Panel, self).__init__()
# can bind from sub classes of Panel as well - different class name in handle_name
self.bindHandler(self.controls.test_button, self._testButtonCB, handler_type = valueChangeHandler)
# can bind multiple versions of same function for repeated ui elements, etc.
for idx in range(0, 10):
self.bindHandler(self.controls["check_box_"+str(idx)], self._testCheckBoxCB,
callback_args = [idx], handler_type = valueChangeHandler, handler_id = idx)
def _testCheckBoxCB(self, *args):
check_box_id = args[0]
print "in _testCheckBoxCB #%d" % check_box_id
def _testButtonCB(self):
"""
Handler for test button
"""
print "in _testButtonCB"
panel = Panel()
Note, that I can also derive further sub-classes from Panel, and any callbacks bound there will get their own unique handler_name, based on class name string.
I am using a simpleWebSocket server class and have a 1 second interval timer that I would like to call methods in a couple of different classes.
the wsscb() class is the handler for the SimpleWebSocketServer(), how can I call a method from the wss() object from another object such as the udt() timer ?
Calling wss.wsscb().myfunc() results in an error: "AttributeError: 'SimpleWebSocketServer' object has no attribute 'wsscb'"
calling wsscb.myfunc() results in: TypeError: unbound method myfunc() must be called with wsscb instance as first argument (got nothing instead)
class wsscb(WebSocket):
def __init__(self, server, sock, address):
WebSocket.__init__(self, server, sock, address)
def myfunc(self):
self.send('some data')
def handleMessage(self):
pass
def handleConnected(self):
pass
class udt(Thread):
def __init__(self, event):
Thread.__init__(self)
self.stopped = event
def run(self):
while not self.stopped.wait(1.00):
wss.wsscb().myfunc()
xxx.yyy()().anotherfunc()
## Main
wss = SimpleWebSocketServer('', 4545,wsscb)
## Start Timer
stopFlag = Event()
self.udt = udt(stopFlag)
self.udt.start()
wss.serveforever()
There are a couple problems.
wss.wsscb() isn't valid. Typing that means you're trying to call a function in wss called wsscb(). wss is a SimpleWebSocketServer, and there is no function called wsscb(). A function is not the same as calling an object.
wsscb() won't work either, because in your class, you're saying it's takes a WebSocket object, which I assume takes some parameters, so you need to pass it those.
I think it would be best to make a subclass of SimpleWebSocketServer (instead of WebSocket), and put your custom function in there. Your comment says "wsscb() is a subclass of SimpleSocketServer", but it is not. It's a subclass of WebSocket.
You also never created an object of type wsscb.
If you can explain what you're specifically trying to achieve, and what myfunc() is, we may be able to help more
Also, you really shouldn't subclass Thread. Scrap the udt class you made and instead
def myfunc(wsscb_object):
while True:
time.sleep(1)
wsscb_object.myfunc()
#whatever else you want
wsscb_object = wsscb(#pass the parameters)
thread = Thread(target=myfunc, args=(some_socket))
thread.start()
You may also want to read up more on inheritance:
python subclasses
http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/
Using inheritance in python
I have this class:
from threading import Thread
import time
class Timer(Thread):
def __init__(self, interval, function, *args, **kwargs):
Thread.__init__()
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.start()
def run(self):
time.sleep(self.interval)
return self.function(*self.args, **self.kwargs)
and am calling it with this script:
import timer
def hello():
print \"hello, world
t = timer.Timer(1.0, hello)
t.run()
and get this error and I can't figure out why: unbound method __init__() must be called with instance as first argument
You are doing:
Thread.__init__()
Use:
Thread.__init__(self)
Or, rather, use super()
This is a frequently asked question at SO, but the answer, in brief, is that the way you call your superclass's constructor is like:
super(Timer,self).__init__()
First, the reason you must use:
Thread.__init__(self)
instead of
Thread.__init__()
is because you are using the class name, and not an object (an instance of the class), so you cannot call a method in the same way as an object.
Second, if you are using Python 3, the recommended style for invoking a super class method from a sub class is:
super().method_name(parameters)
Although in Python 3 is possible to use:
SuperClassName.method_name(self, parameters)
It is an old style of syntax that is not the prefer style.
You just need to pass 'self' as an argument to 'Thread.init'. After that, it works on my machines.