Bind callback to the object instance - python

I'd like to bind a class method to the object instance so that when the method is invoke as callback it can still access the object instance. I am using an event emitter to generate and fire events.
This is my code:
#!/usr/bin/env python3
from pyee import EventEmitter
class Component(object):
_emiter = EventEmitter()
def emit(self, event_type, event):
Component._emiter.emit(event_type, event)
def listen_on(event):
def listen_on_decorator(func):
print("set event")
Component._emiter.on(event, func)
def method_wrapper(*args, **kwargs):
return func(*args, **kwargs)
return method_wrapper
return listen_on_decorator
class TestComponent(Component):
#listen_on('test')
def on_test(self, event):
print("self is " + str(self))
print("FF" + str(event))
if __name__ == '__main__':
t = TestComponent()
t.emit('test', { 'a': 'dfdsf' })
If you run this code, an error is thrown :
File "component.py", line 29, in <module> [0/1889]
t.emit('test', { 'a': 'dfdsf' })
File "component.py", line 8, in emit
Component._emiter.emit('test', event)
File "/Users/giuseppe/.virtualenvs/Forex/lib/python3.4/site-packages/pyee/__init__.py", line 117, in emit
f(*args, **kwargs)
File "component.py", line 14, in method_wrapper
return func(*args, **kwargs)
TypeError: on_test() missing 1 required positional argument: 'event'
This is caused by the missing self when the method on_test is called.

Based on OP's extra requierements presented on the comments to the other answer, there is this alternative approach.
Here, the decorator is used only to mark which methods will work as emitters - and the actual registry of emitters is done only when the class is actually instantiated, based on the methods that were marked.
#!/usr/bin/env python3
from pyee import EventEmitter
class Component(object):
_emiter = EventEmitter()
def __init__(self):
for attr_name in dir(self):
method = getattr(self, attr_name)
if hasattr(method, "_component_emitter_on"):
for event in method._component_emitter_on:
self._emiter.on(event, method)
self.attr_name = method
def emit(self, event_type, event):
Component._emiter.emit(event_type, event)
def listen_on(event):
def listen_on_decorator(func):
print("set event")
func._component_emitter_on = getattr(func, "_component_emitter_on", []) + [event]
return func
return listen_on_decorator
class TestComponent(Component):
#listen_on('test')
def on_test(self, event):
print("self is " + str(self))
print("FF" + str(event))
if __name__ == '__main__':
t = TestComponent()
t.emit('test', { 'a': 'dfdsf' })
(Note I had also removed a redundant indirection level on your decorator - if decorators won't replace the callable itself, just make annotations to it (or with it), they don't need to create another callable)

The instance-bound method does not exist, as it is hard to imagine otherwise, at the time the class body is parsed - which is when decorators are applied.
That means your on_test method behaves just like a function, not being "aware" of it's class or instance at that point. When a method is retrieved from an object instance, Python does create an special callable (with "method" type) that essentially will insert the self parameter in a call to the original function.
One way to make it work is to decorate this callable (the bound method itself) instead of the raw function. Of course, it only exists at the time the class is instantiated.
Fortunately, in Python, decorators are mostly a syntactic shortcut to a function call passing the decorated function as a parameter - so you can just rewrite your TestComponent class more or less like this:
class TestComponent(Component):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.on_test = listen_on('test')(self.on_test)
def on_test(self, event):
print("self is " + str(self))
print("FF" + str(event))
Note that as we re-assign the self.on_test instance member after decoration, it ceases to behave as a method when called through the instance - it will be a mere function call, without magic inserting of self - however, the self parameter is already bound to that callable on the moment self.on_test is retrieved and passed as a parameter to the decorator, on the right side of that line.
** alternative **
The comments suggests the above use does not look like the decorator is used - one can rewrite that to actually use the decorator syntax, just doing this - although this will hide the method from static code checkers, such as IDE autocompletion engines, and linters:
class TestComponent(Component):
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
#listen_on('test')
def on_test(self, event):
print("self is " + str(self))
print("FF" + str(event))
self.on_test = on_test

Related

Issues passing self to class decorator in python

I am new to decorators but ideally I wan to use them to simply define a bunch of class functions within class OptionClass, each representing some particular option with a name and description and if it's required. I don't want to modify the operation of the class function at all if that makes sense, I only want to use the decorator to define name, description, and if it's required.
Problem 1: I construct an OptionClass() and I want to call it's option_1. When I do this I receive a TypeError as the call decorator is not receiving an instance of OptionClass. Why is this? When I call option_1 passing the instance of OptionClass() it works. How do I call option_1 without needing to always pass the instance as self.
The error when received is:
Traceback (most recent call last):
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 110, in <module>
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 80, in __call__
return self.function_ptr(*args, **kwargs)
TypeError: option_1() missing 1 required positional argument: 'test_text'
Problem 2: How would I run or call methods on the decorator to set_name, set_description, set_required?
Problem 3: Although this is a sample I intend to code an option class using async functions and decorate them. Do I need to make the decorator call be async def __call__() or is it fine since it's just returning the function?
class option_decorator(object):
def __init__(self, function_pt):
self.function_ptr = function_pt
self.__required = True
self.__name = ""
self.__description = ""
def set_name(self, text):
self.__name = text
def set_description(self, text):
self.__description = text
def set_required(self,flag:bool):
self.__required = flag
def __bool__(self):
"""returns if required"""
return self.__required
def __call__(self, *args, **kwargs):
return self.function_ptr(*args, **kwargs)
def __str__(self):
"""prints a description and name of the option """
return "{} - {}".format(self.__name, self.__description)
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
#option_decorator
def option_2(self):
print("option 2")
def get_all_required(self):
"""would return a list of option functions within the class that have their decorator required flag set to true"""
pass
def get_all_available(self):
"""would return all options regardless of required flag set"""
pass
def print_all_functions(self):
"""would call str(option_1) and print {} - {} for example"""
pass
a = OptionClass()
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
print(a.option_1(a,"test")) #Prints: option test
Problem 1
You implemented the method wrapper as a custom callable instead of as a normal function object. This means that you must implement the __get__() descriptor that transforms a function into a method yourself. (If you had used a function this would already be present.)
from types import MethodType
class Dec:
def __init__(self, f):
self.f = f
def __call__(self, *a, **kw):
return self.f(*a, **kw)
def __get__(self, obj, objtype=None):
return self if obj is None else MethodType(self, obj)
class Foo:
#Dec
def opt1(self, text):
return 'foo' + text
>>> Foo().opt1('two')
'footwo'
See the Descriptor HowTo Guide
Problem 2
The callable option_decorator instance replaces the function in the OptionClass dict. That means that mutating the callable instance affects all instances of OptionClass that use that callable object. Make sure that's what you want to do, because if you want to customize the methods per-instance, you'll have to build this differently.
You could access it in class definition like
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
option_1.set_name('foo')
Problem 3
The __call__ method in your example isn't returning a function. It's returning the result of the function_ptr invocation. But that will be a coroutine object if you define your options using async def, which you would have to do anyway if you're using the async/await syntax in the function body. This is similar to the way that yield transforms a function into a function that returns a generator object.

Python Decorator cannot access called method

I am trying to write a decorator for a method that will call a second method. When I run the code I receive the error:
AttributeError: 'Backoff' object has no attribute 'formatter'
Simplified, the code is:
class Backoff:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
n = 1
while n < 11:
try:
return self.f(self, *args, **kwargs)
except FooError as e:
<handle error>
time.sleep((2 ** n) + (random.randint(0, 1000) / 1000))
n = n + 1
class SomeClass:
def __init__(self):
pass
#Backoff
def first_method(self, foo, bar):
return self.formatter(foo, bar)
def formatter(self, x, y):
return some_function_to_format(x, y)
How can I pass the second method to the first method in a way that the decorator can recognise it?
Any help would be amazing!
You are passing a method to Backoff, assigning it as an instance variable, and then calling it. This not bound to an instance until it is being called in Backoff.__call__, where it is then bound to an instance of Backoff which does not have a formatter property.
There might be a straightforward solution to this depending on what your instance method needs access to (i.e. if it only needs to reference a class or static method, it can just call the fully qualified name directly). However, if you need the instance method to reference an instance property, I would suggest not using a class decorator at all. Using a function decorator will not run into these issues, you can create a closure and return a function with the same call signature.

Accessing self in a function attribute

I'm trying to add a decorator that adds callable attributes to functions that return slightly different objects than the return value of the function, but will execute the function at some point.
The problem I'm running into is that when the function object is passed into the decorator, it is unbound and doesn't contain the implicit self argument. When I call the created attribute function (ie. string()), I don't have access to self and can't pass it into the original function.
def deco(func):
"""
Add an attribute to the function takes the same arguments as the
function but modifies the output.
"""
def string(*args, **kwargs):
return str(func(*args, **kwargs))
func.string = string
return func
class Test(object):
def __init__(self, value):
self._value = 1
#deco
def plus(self, n):
return self._value + n
When I go to execute the attribute created by the decorator, this is the error I get, because args doesn't contain the self reference.
>>> t = Test(100)
>>> t.plus(1) # Gets passed self implicitly
101
>>> t.plus.string(1) # Does not get passed self implicitly
...
TypeError: plus() takes exactly 2 arguments (1 given)
Is there a way to create a decorator like this that can get a reference to self? Or is there a way to bind the added attribute function (string()) so that it also gets called with the implicit self argument?
You can use descriptors here:
class deco(object):
def __init__(self, func):
self.func = func
self.parent_obj = None
def __get__(self, obj, type=None):
self.parent_obj = obj
return self
def __call__(self, *args, **kwargs):
return self.func(self.parent_obj, *args, **kwargs)
def string(self, *args, **kwargs):
return str(self(*args, **kwargs))
class Test(object):
def __init__(self, value):
self._value = value
#deco
def plus(self, n):
return self._value + n
so that:
>>> test = Test(3)
>>> test.plus(1)
4
>>> test.plus.string(1)
'4'
This warrants an explanation. deco is a decorator, but it is also a descriptor. A descriptor is an object that defines alternative behavior that is to be invoked when the object is looked up as an attribute of its parent. Interestingly, bounds methods are themselves implemented using the descriptor protocol
That's a mouthful. Let's look at what happens when we run the example code. First, when we define the plus method, we apply the deco decorator. Now normally we see functions as decorators, and the return value of the function is the decorated result. Here we are using a class as a decorator. As a result, Test.plus isn't a function, but rather an instance of the deco type. This instance contains a reference to the plus function that we wish to wrap.
The deco class has a __call__ method that allows instances of it to act like functions. This implementation simply passes the arguments given to the plus function it has a reference to. Note that the first argument will be the reference to the Test instance.
The tricky part comes in implementing test.plus.string(1). To do this, we need a reference to the test instance of which the plus instance is an attribute. To accomplish this, we use the descriptor protocol. That is, we define a __get__ method which will be invoked whenever the deco instance is accessed as an attribute of some parent class instance. When this happens, it stores the parent object inside itself. Then we can simply implement plus.string as a method on the deco class, and use the reference to the parent object stored within the deco instance to get at the test instance to which plus belongs.
This is a lot of magic, so here's a disclaimer: Though this looks cool, it's probably not a great idea to implement something like this.
You need to decorate your function at instantiation time (before creating the instance method). You can do this by overriding the __new__ method:
class Test(object):
def __new__(cls, *args_, **kwargs_):
def deco(func):
def string(*args, **kwargs):
return "my_str is :" + str(func(*args, **kwargs))
# *1
func.__func__.string = string
return func
obj = object.__new__(cls, *args_, **kwargs_)
setattr(obj, 'plus', deco(getattr(obj, 'plus')))
return obj
def __init__(self, value):
self._value = 1
def plus(self, n):
return self._value + n
Demo:
>>> t = Test(100)
>>> t.plus(1)
>>> t.plus.string(5)
>>> 'my_str is :6'
1. Since python doesn't let you access the real instance attribute at setting time you can use __func__ method in order to access the real function object of the instance method.

How To Call A Class Function From A Variable

I need a class router (for lack of a better word). The router needs to instantiate a class & call a function of that class instance based on variables passed to it.
How do I properly define the class function?
How do I properly call the class function?
Example code:
class ClassWorker1:
def function_1(self):
print('1a')
def function_2(self):
print('2a')
def function_3(self):
print('3a')
class ClassWorker2:
def function_1(self):
print('1b')
def function_2(self):
print('2b')
def function_3(self):
print('3b')
class ClassRouter(object):
def __init__(self, class_name, class_function):
self.class_instance = class_name()
self.class_function = class_function
self.main()
def main(self):
# how should I call the class function here?
self.class_instance.class_function()
return
a = 1
b = 1
if a == 1:
class_name = ClassWorker1
else:
class_name = ClassWorker1
if b == 1:
# Strings won't work as class function definition
# I won't know the class at this point. I will only know
# the shared function name at this point.
# how could this class function be defined directly?
class_function = 'function_1'
elif b == 2:
class_function = 'function_2'
else:
class_function = 'function_3'
ClassRouter(class_name, class_function)
I need a class router (for lack of a better word).
Are you sure you need a class for this ?
The router needs to instantiate a class & call a function of that class instance
When it belongs to a class or instance, a function is usually named a "method". Not really important but it makes things clearer. Also, an "instance" is obviously always, by definition, an instance of a class ;)
How do I properly define the class function?
How do I properly call the class function?
Does the router really have to be a class ? But anyway...
There are a couple distinct issues here (I of course assume you need something that's generic enough).
The first one is that your class (the one that will be instanciated by the "router") constructor may need some args - position or named or both. If it's the router's duty to instanciate the class (but should it be ?), you'll have to pass those args (both position and named) to the router. And since your router has to be generic (else it's useless) you cannot explicitely name these args in your router's constructor.
Hopefully, Python has a way to "unpack" tuples (for position args) and dicts (for named args) when calling a function, using respectively the * and ** operators at call time, ie:
def somefunc(arg1, arg2, arg3="foo", arg4=None):
print arg1, arg2, arg3, arg4
args = ("one", "two", "three")
kwargs = {"arg4": "four"}
somefunc(*args, **kwargs)
This let you pass arguments to a function in a generic way.
So if you want your router to be in charge of instanciating the "target" class, you'll have to support this:
class Router(object):
def __init__(self, cls, clsargs=None, clskwargs=None):
if clsargs is None:
clsargs = ()
if clskwargs is None:
clskwargs = {}
self._obj = cls(*clsargs, **clskwargs)
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker, clsargs=("foo",))
# or
r = Router(Worker, clskwargs={"name":"foo"})
Now note that at this point you really don't gain anything (except for more code) from having the router instanciating the Worker - since you need to have the Worker class and it's constructor's args to instanciate the router, you could as well just instanciate the Worker yourself and pass the Worker instance to the router:
Since you must have a reference to the class passed to the router (else you can't pass it ), you could as well
class Router(object):
def __init__(self, obj):
self._obj = obj
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker("foo"))
# or
r = Router(Worker(name="foo"))
The cases where it would make sense to have the router instanciate the worker are:
1/ if the Worker's constructor arguments are not known when the router is instanciated and are to be passed later (which requires a distinct router method to pass those args)
2/ if the Worker's instanciation is very costly and you're not even sure you'll really need it, in which case you want to wait until the router's "main" method is called to instanciate the worker.
The second issue is "how do I get the worker's method by name". This one has already been answered by Lukas: you use getattr(obj, attrname).
The third issue is "if my worker method needs arguments, how do I pass them". This is the same problem as with the worker's constructor arguments, so the solution is obviously the same. Depending on the concrete use case, you'll have to pass those args either when instanciating the router or when calling it's "main" method.
wrt/ this "main" method, remember that you can define your own callable types by implementing the __call__ method, ie
class NotAFunc(object):
def __init__(self, wot):
self.wot = wot
def __call__(self, count):
print self.wot * count
notafunc = NotAFunc("wot ? ")
notafunc(42)
So it might make sense to use this as your router's "main" method
Now do you really need a router class at all ? Python functions are object on their own (so a function can take a function and/or return a function), and moreover act as closures (a closure is a function that "captures" part of the environment where it's been defined):
def route(instance, methodname, methargs=None, methkwargs=None):
method = getattr(instance, methodname)
if methargs is None:
methargs = ()
if methkwargs is None:
methkwargs = {}
def func():
return method(*methargs, **methkwargs)
return func
class Worker(object):
def __init__(self, name):
self.name = name
def work(self, count):
return [self.name for i in range(count)]
r = route(Worker("foo"), "work", (42,))
print r()
Note that while I kept your "router" term, most of what I described above are known patterns. You may want to search for "proxy", "proxy method", and (for the last exemple) "partial evaluation".
You are looking for dynamic attribute lookup.
class C:
def c1(self, x):
return 2*x
instance = C()
method_name = 'c1'
method = getattr(instance, method_name)
print(method(1)) # call method and print result
You'll need to override the __new__ method of your (new-style!) class.
class ClassRouter(object):
def __new__(self, class_name, *args):
if arg=="Class1":
new_instance = ClassWorker1(*args)
new_instance.method()
return new_instance
elif arg=="Class2":
return ClassWorker2(*args)

How to intercept a method call which doesn't exist?

I want to create a class that doesn't gives an Attribute Error on call of any method that may or may not exists:
My class:
class magic_class:
...
# How to over-ride method calls
...
Expected Output:
ob = magic_class()
ob.unknown_method()
# Prints 'unknown_method' was called
ob.unknown_method2()
# Prints 'unknown_method2' was called
Now, unknown_method and unknown_method2 doesn't actually exists in the class, but how can we intercept the method call in python ?
Overwrite the __getattr__() magic method:
class MagicClass(object):
def __getattr__(self, name):
def wrapper(*args, **kwargs):
print "'%s' was called" % name
return wrapper
ob = MagicClass()
ob.unknown_method()
ob.unknown_method2()
prints
'unknown_method' was called
'unknown_method2' was called
Just in case someone is trying to delegate the unknown method to an object, here's the code:
class MagicClass():
def __init__(self, obj):
self.an_obj = obj
def __getattr__(self, method_name):
def method(*args, **kwargs):
print("Handling unknown method: '{}'".format(method_name))
if kwargs:
print("It had the following key word arguments: " + str(kwargs))
if args:
print("It had the following positional arguments: " + str(args))
return getattr(self.an_obj, method_name)(*args, **kwargs)
return method
This is super useful when you need to apply the Proxy pattern.
Moreover, considering both args and kwargs, allows you to generate an interface totally user friendly, as the ones that use MagicClass treat it as it was the real object.
Override __getattr__; see http://docs.python.org/reference/datamodel.html

Categories