Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I bind a function to an object instance using decorator?

I'm trying to bind a function to an object instance. For example, I have an object and I'm trying to bind a function to the condition attribute. The thing is that I want to do that using a decorator:

class Transition(object):
    def __init__(self, enter, exit, condition=None):
        if not isinstance(enter, State) or not isinstance(exit, State):
            raise TypeError("Parameters should be an instance of the State class")
        self.enter = enter
        self.exit = exit
        self.condition = None
        self.effect = None

    def __repr__(self):
        print("Going from {} to {} with condition {} and effect {}".format(self.enter, self.exit, self.condition.__name__, self.effect.__name__)) 

    def __eq__(self, other):
        return self.enter == other.enter and self.exit == other.exit

    def is_enpoint(self, endpoints):
        """
        :parameter --> 'endpoints' is a tuple indicating where the transition flows(from, to) 
        :return --> boolean
        :description --> check if the given endpoints are valid 
        """
        return self.enter == endpoints[0] and self.exit == endpoints[1]

Then I bind a function to the object's instance.

@bind
my_condition():
    return True

After this, we should have a reference to the given function if we look at the object's instance condition attribute

Edit 1:

f = Transition()
f1 = Transition()

@bind(f)
def condition1():
    return True

@bind(f1)
def condition2():
    return False

The f instance should have a reference to the condition1 function and the f2 instance should have a reference to the condition2 for the condition attribute

like image 866
Alexandre Maneta Avatar asked Apr 15 '26 17:04

Alexandre Maneta


1 Answers

The decorator, of course, must get as a parameter the instance it is to make the bind to - and, the code will be much cleaner, if the decorated function itself have one parameter to be passed as the instance it is bound too: the equivalent to self if it were defined as a method. Python won't insert it automatically, but it may be called self so it is easily readable;

class Transition:
   ...

f = Transition

def bind(instance):
    def decorator(func):
        def wrapper (*args, **kwargs):
              return func(instance, *args, **kwargs)
        setattr(instance, func.__name__, wrapper)
        return wrapper
    return decorator

@bind(f)
def condition(self, ...):
    ...

If you want a flatter decorator, you can use functools.partial - and then I also use functools.wraps, as nice decorators should use it anyway:

import functools

...

def bind(func, instance=None):
    if instance is None:
         return functools.partial(bind, instance=func)
    @functools.wraps(func)
    def wrapper(*args, **kw):
         return func(instance, *args, **kw)
    setattr(instance, func.__name__, wrapper)
    return wrapper

This works because in Python, when a function is attributed directly to an instance, it behaves exactly like any ordinary attribute: Python will retrieve the function, and then call it, with no modifications - not changing it to method, neither inserting a self argument). We do that by holding the instance as a nonlocal variable inside the decorator code.

like image 117
jsbueno Avatar answered Apr 17 '26 06:04

jsbueno



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!