I am trying to create a class (MySerial
) that instantiates a serial object so that I can write/read to a serial device (UART). There is an instance method that is a decorator which wraps around a function that belongs to a completely different class (App
). So decorator is responsible for writing and reading to the serial buffer.
If I create an instance of MySerial
inside the App
class, I can't use the decorator instance method that is created from MySerial
.
I have tried foregoing instance methods and using class methods as explained in this second answer, but I really need to instantiate MySerial
, thus create an instance using __init__
.
How can this be accomplished? Is it impossible?
class MySerial():
def __init__(self):
pass # I have to have an __init__
def write(self):
pass # write to buffer
def read(self):
pass # read to buffer
def decorator(self, func):
def func_wrap(*args, **kwargs):
self.write(func(*args, **kwars))
return self.read()
return func_wrap
class App():
def __init__(self):
self.ser = MySerial()
@self.ser.decorator # <-- does not work here.
def myfunc(self):
# 'yummy_bytes' is written to the serial buffer via
# MySerial's decorator method
return 'yummy_bytes'
if __name__ == '__main__':
app = App()
You can use a staticmethod
to wrap decorator
. The inner func_wrap
function of decorator
contains an additional parameter in its signature: cls
. cls
can be used to access the ser
attribute of the instance of App
, and then the desired methods write
and read
can be called from cls.ser
. Also, note that in your declarations, MySerial.write
takes no paramters, but is passed the result of the wrapped function. The code below uses *args
to prevent the TypeError
which would otherwise be raised:
class MySerial():
def __init__(self):
pass # I have to have an __init__
def write(self, *args):
pass # write to buffer
def read(self):
pass # read to buffer
@staticmethod
def decorator(func):
def func_wrap(cls, *args, **kwargs):
cls.ser.write(func(cls, *args, **kwargs))
return cls.ser.read()
return func_wrap
class App():
def __init__(self):
self.ser = MySerial()
@MySerial.decorator
def myfunc(self):
# 'yummy_bytes' is written to the serial buffer via
# MySerial's decorator method
return 'yummy_bytes'
App().myfunc()
The reason this does not work is because you are refering to self
in the class body, where it is not defined. Here are two solutions.
If you store the MySerial
instance as a class attribute, then it sill be accessible in the class body:
class App():
ser = MySerial()
@ser.decorator
def myfunc(self):
return 'yummy_bytes'
Or if you need a different MySerial
instance for every App
instance, then you will need to wait for the instance to be created to define an instance attribute my_func
. This means the function is decorated dynamically on every instance creation, in which case, the @
decorator syntax must be replaced by a function call.
class App():
def __init__(self):
self.ser = MySerial()
self.my_func = self.ser.decorator(self.myfunc)
def myfunc(self):
return 'yummy_bytes'
This solution generalizes to decorating multiple methods or conditionally deactivating serializing, say in a test environment.
import env
class App():
def __init__(self):
self.ser = MySerial()
to_decorate = [] if env.test else ['myfunc']
for fn_name in to_decorate:
fn = getattr(self, fn_name)
setattr(self, fn_name, self.ser.decorator(fn))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With