0

I have this code:

def check_karma(needed_karma, user_karma):
    # check karma or exit
    return True

def hello_world():
    user_karma = 50
    check_karma(20, user_karma)
    print "Hello World"

Can I use decorators here?, something like this:

...
@check_karma(20, user_karma)
def hello_world():
    user_karma = 50
    print "Hello World"

I don't know if I can access the data inside the function, becouse I wrote @check_karma(20) successfully in a test script.

Cyrus
  • 131
  • 8
  • making decorators with arguments isn't that simple... http://stackoverflow.com/questions/5929107/python-decorators-with-parameters – Dunno Mar 24 '14 at 21:16
  • 1
    No, decorators cannot access local variables. You could make `user_karma` an argument to `hello_world()` but otherwise what you want to do is impossible. – Bakuriu Mar 24 '14 at 21:21
  • `check_karam` looks like what I often use `assert` for – cmd Mar 24 '14 at 21:24

1 Answers1

0

Yes, a decorator can be used in this instance (and it might actually be benefitial), however, it's not as simple as defining a function. In this case, what you would want to do is define what is known as a functor. A functor is a class that works as a function would.

In example, say you had the class Apple. You might instantiate an object of this class by doing the following:

apple = Apple(color='red')

And now, if this were a functor, you could take this a step further by using the apple object as if it were a function by calling it with apple(). This can be used to create the decorator you are trying to make. You will initialize the check_karma class in the decorator's definition like so:

@check_karma(needed_karma)
def hello_world():
    ...

This is because a decorator must be a function that returns another function. What the snippet above is essentially doing is this:

def hello_world():
    ...
hello_world = check_karma(needed_karma)(hello_world)

and then every time we call hello_world, we are calling the function that was returned by the check_karma functor. user_karma should probably requested from elsewhere.

Here is an example of how you might apply this in code:

user_karma = 50  # pretend this is where you store `user_karma`.

class KarmaRestrictor:
    def __init__(self, needed_karma):
        self.needed_karma = needed_karma

    def __call__(self, restricted_func):
        # This is the actual decoration routine. This `__call__` descriptor
        # is what makes this class a **functor**.

        def _restricted_func(*args, **kwargs):
            if self.get_user_karma() >= self.needed_karma:
                return restricted_func(*args, **kwargs)
            else:
                # TODO: Maybe have a failure routine?
                pass

        return _restricted_func

    def get_user_karma(self):
        return user_karma  # wherever you store this.

check_karma = KarmaRestrictor  # give this decorator an alias.

@check_karma(20)
def hello_world():
    print 'Hello World'