Can I store an instance method as an attribute in Python? - python

I have a Python class that allows a user to register a callback. I am trying to provide a default callback but I'm not sure how to do it.
First attempt:
class MyClass:
callback = printing_callback
def register_callback(self, callback):
self.callback = callback
def printing_callback(self, message):
print(f"Message: {message}")
def notify(self, message):
self.callback(message)
This gave me an 'unresolved reference' error for printing_callback
Second attempt:
I tried changed the line to callback = self.printing_callback. This gave me an 'unresolved reference' error for self
Third attempt:
callback = lambda message: print(f"Message: {message}")
which gave me this warning: "PEP 8: E731 do not assign a lambda expression, use a def"
Is there a way to initialize callback to a default?
Update
I found a way to set the default method and that is not to have printing_callback be an instance method - which makes sense. This appears to compile without warnings
def printing_callback(message):
print(f"Message: {message}")
class MyClass:
callback = printing_callback
def register_callback(self, callback):
self.callback = callback
def notify(self, message):
self.callback(message)
But now the when printing_callable is called it is called with an extra argument - the MyClass instance that called it.
I can change the signature to printing_callback(myClass, message) and the code works. Are there cleaner ways to do this than to just have an extra parameter?

Set the default on initialization.
def printing_callback(message):
print(f"Message: {message}")
class MyClass:
def __init__(self, callback=printing_callback):
self.callback = callback
def notify(self, message):
self.callback(message)
As far as I can tell, there's no reason for callback to be a class attribute, so make it an instance attribute, and that avoids it being registered as a method.
If you need to change it later, you can simply change the callback attribute instead of using the setter register_callback():
m = MyClass()
m.notify('Hello!') # -> Message: Hello!
m.callback = print
m.notify('Hi!') # -> Hi!

In first attempt, it's obvious that you have no reference to printing_callback, you didn't define it.
In second: self parameter is which get filled by python, when you call that method on an instance of the class. It points to the newly created object which is the class instance ofcourse. note that it is local to the methods, not inside the body of your class. so no reference again.
The structure you are looking for is :
def printing_callback(self, message):
print(f"Message: {message}")
class MyClass:
callback = printing_callback
def register_callback(self, callback):
self.callback = callback
def notify(self, message):
self.callback(message)
obj = MyClass()
obj.callback("testing")
Note I added an extra parameter self (the actual name doesn't matter).This is because we call that function on an instance, so again python fill the first argument with the reference to that instance. This is why I named it self.

Related

Why are there warnings in these decorated methods?

I am string with decorators and the first use I have is to wrap a HTTP call to account for failed connections. The working code is as follows:
import requests
class Gotify:
def __init__(self):
self.url = "https://postman-echo.com/status/200"
def ensure_safe_call(caller):
def wrapper(*args, **kwargs):
try:
r = caller(*args, **kwargs)
r.raise_for_status()
except Exception as e:
try:
print(f"cannot reach gotify: {e}: {r.text}")
except NameError:
print(f"cannot reach gotify: {e} (the response r does not exist)")
else:
print("OK notified gotify of result change")
return wrapper
#ensure_safe_call
def send(self, title, message):
return requests.get(self.url)
Gotify().send("hello", "world")
This correct displays OK notified gotify of result change.
When editing this in PyCharm, I get two warning which I do not understand:
and
What do they mean in the context of my decorators (there are none when I do not use decorators)
class Gotify:
def __init__(self):
self.url = "https://postman-echo.com/status/200"
def ensure_safe_call(caller):
Because ensure_safe_call is a class method, the first argument (in your case caller) is actually the self argument, the instance of the clas object--Gotify.
Hence the warning message about the Gotify object not being callable (it's not callable because you have not overridden the __call__ class method in your Gotify class)
Function ensure_safe_call lacks a positional argument -- this is because ensure_safe_call only takes in the self argument, and doesn't specify any actual input arguments (recall that caller == self given the way you have it defined). Thus, your decorator ensure_safe_call cannot wrap anything, because it's accepting no position arguments.
You need to define a positional argument
def ensure_safe_call(self, caller):
...

Under what circumstances is a class member function not passed self as the first parameter?

Problem
I am using a library to facilitate client side websocket communication with a server.
The websocket library allows you to specify call back functions for when the socket opens, closes, errors or receives a message
If I set my callback functions to be instance functions of another class, then they need to be passed the self parameter when the are called.
I had understood that if you call a class instance method it will always be passed self as the first parameter. However,my callbacks are not getting passed self
For example
from websocket import WebSocketApp
import websocket
class X(object):
def run(self):
self.ws = WebSocketApp('wss://api.bitfinex.com/ws/2'
,on_open=self.on_open
,on_message=self.on_message
,on_error=self.on_error
,on_close=self.on_close)
websocket.enableTrace(True)
self.ws.run_forever()
def on_open(self, ws):
print('open')
def on_close(self, ws):
print('close')
def on_message(self, ws, message):
print('message')
def on_error(self, ws, error):
print('error')
if __name__=='__main__':
x = X().run()
Output
error from callback <bound method X.on_open of <__main__.X object at 0x7fd7635e87f0>>: on_open() missing 1 required positional argument: 'ws'
File "/home/arran/.local/lib/python3.6/site-packages/websocket/_app.py", line 343, in _callback
callback(*args)
I am probably missing something basic here. But any help would be greatly appreciated
Edit
Looks like this might be a version specific issue with the websocket-client library https://github.com/home-assistant/home-assistant/issues/17532
I have downgraded to an earlier version and fixed my problem.
I would still be curious to know how this issue can arise though. My understanding was that class instance methods will always be passed self as the first parameter
It looks to be an issue with the WebSocket class not passing the ws argument that your on_open method expects. I tried to reproduce it with my own dummy class, and it works fine.
class WS:
def __init__(self, on_call):
self.on_call = on_call
def call(self):
print("hi")
self.on_call(self)
class X:
def on_call(self, ws):
print(ws)
def run(self):
self.ws = WS(self.on_call)
self.ws.call()
X().run()
hi
<__main__.WS instance at 0x029AB698>
I am probably missing something basic here.
No, you were spot on. However, the on_open callback does not get called with the ws argument, although it should according to the documentation:
class WebSocketApp(object):
(...)
on_open: callable object which is called at opening websocket.
this function has one argument. The argument is this class object.
(...)
This is a known bug that was closed despite some discussion around the way it was fixed.
would still be curious to know how this issue can arise though.
I guess it's an honest mistake in an attempted bug fix. As there is no test for your particular scenario, it did not get caught.
I have downgraded to an earlier version and fixed my problem
Please kindly submit a bug report or write a pull request to fix the problem.
My understanding was that class instance methods will always be passed self as the first parameter
Yes, your understanding is correct. Here is an example mirroring what you tried.
class Server(object):
def __init__(self, callback):
self.callback = callback
def run(self):
self.callback(5)
class Client(object):
def on_message(self, n):
print("got", n)
client = Client()
server = Server(client.on_message)
server.run()

how to pass a variable of one class to multiple classes in python

I'm having problem with passing value/object from one class to another. My code as below. When I invoke the method mainFunction() , all the calls work fine, but it is failing self.stub.call_method() . Not sure why. When I declare the stub as a global variable, and then use it it works perfectly fine. The Bar() is another class which has remote invocation methods and one being call_method() . Any help on this will be greatly appreciated.
The failure I meant is, the call reaches till call_method() and the call_method has some http invocation and it is unable to do the http invocation and throws HTTP Exception.
class Command(object):
def dosomething(self):
return 0
class Connect(Command):
def __init__(self, value1, stub):
self.value1 = value1
self.stub = stub
def dosomething(self):
self.stub.call_method()
class Osclass(object):
def __init__(self, val1):
self.val1 =val1
stub=Bar(value)
self.stub = stub
def activemethod(self):
return Connect(self.val1, self.stub)
def mainFunction(val1, Osclass):
ret_value = Osclass.activemethod()
ret_value.execute()

Python - call method in subclass

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

Accessing function arguments from decorator

I have a Request handler and a decorator, I would like to work with the self object inside the decorator
class MyHandler(webapp.RequestHandler):
#myDecorator
def get(self):
#code
Update: Please notice the difference between the first and second self
class myDecorator(object):
def __init__(self, f):
self.f = f
def __call__(self):
#work with self
MyHandler > get ( function ) > self ( argument )
myDecorator > __call__ ( function ) > self ( argument )
the self arguments mentioned above are different. My intention is to access the first self from inside __call__ function, or find a way to do something similar.
Hi can I access MyHandlers self argument from get function inside the decorator?
Update2: I want to implement a decorator to work with a custom login in google app engine:
I have a class ( requestHandler ):
class SomeHandler(webapp.RequestHandler):
#only_registered_users
def get(self):
#do stuff here
And I want to decorate the get function in order to check out if the user is logged in or not:
from util.sessions import Session
import logging
class only_registered_users(object):
def __init__(self, f):
self.f = f
def __call__(self):
def decorated_get(self):
logging.debug("request object:", self.request)
session = Session()
if hasattr(session, 'user_key'):
return self.f(self)
else:
self.request.redirect("/login")
return decorated_get
I know if a user is logged in if has the property 'user_key' in a session Object.
That's the main goal I'm interested in on this specific case
Let me know your suggestions / opinions if I'm doing something wrong!
Thanks!
I'm not entirely clear what it is you want, but if you just want to use the decorated function's arguments, then that is exactly what a basic decorator does. So to access say, self.request from a decorator you could do:
def log_request(fn):
def decorated_get(self):
logging.debug("request object:", self.request)
return fn(self)
return decorated_get
class MyHandler(webapp. RequestHandler):
#log_request
def get(self):
self.response.out.write('hello world')
If you are trying to access the class the decorated function is attached to, then it's a bit tricker and you'll have to cheat a bit using the inspect module.
import inspect
def class_printer(fn):
cls = inspect.getouterframes(inspect.currentframe())[1][3]
def decorated_fn(self, msg):
fn(self,cls+" says: "+msg)
return decorated_fn
class MyClass():
#class_printer
def say(self, msg):
print msg
In the example above we fetch the name of the class from the currentframe (during the execution of the decorator) and then store that in the decorated function. Here we are just prepending the class-name to whatever the msg variable is before passing it on to the original say function, but you can use your imagination to do what ever you like once you have the class name.
>>> MyClass().say('hello')
MyClass says: hello
source
def p_decorate(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper
#p_decorate
def get_text(name):
return "lorem ipsum, {0} dolor sit amet".format(name)
print get_text("John")
# Outputs <p>lorem ipsum, John dolor sit amet</p>
Try this approach: Can a Python decorator of an instance method access the class?
Not the exact same question but you should be able to use the same approach to create a reference to self or a reference to a dictionary with objects of a certain class in it that you can get out in your decorator.
import random
#decorator to the get function in order to check out if the user is logged in or not
def only_registered_users(func):
def wrapper(handler):
print 'Checking if user is logged in'
if random.randint(0, 1):
print 'User is logged in. Calling the original function.'
func(handler)
else:
print 'User is NOT logged in. Redirecting...'
# redirect code here
return wrapper
class MyHandler(object):
#only_registered_users
def get(self):
print 'Get function called'
m = MyHandler()
m.get()
The self argument to __call__ will be populated with the instance the decorated method is being called on. There's no way to access the decorator object from here - __call__ is a bound method, and what you're asking for would require it to be, in effect, 'doubly bound'. Python doesn't support this, so the decorator instance gets replaced with the decorated function's first argument.
The easiest way to work around this is to use nested functions, as #Chris recommends.

Categories