smac89
smac89

Reputation: 43206

Python write decorator for static method

I am trying to make a function that will cache some of it's attributes as these take some computation to return a result. Since it is the same result, I wanted to cache this in some way that allows for reusing.

Some code to put this in perspective:

This is what my proposed decorator looks like:

def cachedAttributes(func, **kwargs):
    func.__dict__.update(kwargs)
    return func

The class method I am trying to decorate:

class someclass:
    @staticmethod
    def method(*args):
        # compile regex operation
        # compile regex operation
        # compile regex operation

        # compute and return something
        ...
        pass

This is how I am using the decorator

class someclass:
    @staticmethod
    @cachedAttributes(method, doctyperegex = re.compile(r'<!DOCTYPE.*?>\s*', re.S|re.U),
                  htmlregex = re.compile(r'<html.*?>\s*', re.S|re.U),
                  metaregex = re.compile(r'<meta(.*?)>', re.S|re.U))
    def method(*args):
        # compile regex operation
        # compile regex operation
        # compile regex operation

        # compute and return something
        ...
        pass

So the above is a representation of what I am working with. I have a class which has a static method that uses some regex objects to do some computations, and I want to cache the result of the compile regex operations. However this gives me the following error:

@staticmethod
NameError: name 'method' is not defined

I know this is due to having not defined the method before trying to decorate it, but how else can I do this? Is there a builtin that can do this for me?

Upvotes: 0

Views: 353

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1124238

Your decorator will be passed the method function when Python applies the decorator. You do not need give your decorator factory the method as an argument:

@cachedAttributes(doctyperegex = re.compile(r'<!DOCTYPE.*?>\s*', re.S|re.U),
              htmlregex = re.compile(r'<html.*?>\s*', re.S|re.U),
              metaregex = re.compile(r'<meta(.*?)>', re.S|re.U))

Your cachedAttributes() callable then returns the actual decorator:

def cachedAttributes(**kwargs):
    def decorator(func):
        func.__dict__.update(kwargs)
        return func
    return decorator

So Python calles cachedAttributes(), which returns a decorator. That is then called passing in the function object and the return values is used as the replacement of the decorated function. That can be the original function object, like you are doing here.

Upvotes: 1

Claudiu
Claudiu

Reputation: 229561

You need to follow this common pattern: you want a function which takes the intended parameters and returns a decorator function.

def cachedAttributes(**kwargs):
    def decorator(func):
        func.__dict__.update(kwargs)
        return func
    return decorator

Then:

class someclass:
    @staticmethod
    @cachedAttributes(doctyperegex = re.compile(r'<!DOCTYPE.*?>\s*', re.S|re.U),
              htmlregex = re.compile(r'<html.*?>\s*', re.S|re.U),
              metaregex = re.compile(r'<meta(.*?)>', re.S|re.U))
    def method(*args):
        # compile regex operation
        # compile regex operation
        # compile regex operation

        # compute and return something
        ...
        pass

Upvotes: 1

Related Questions