Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8130625
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 6, 20262026-06-06T08:47:09+00:00 2026-06-06T08:47:09+00:00

Recently, the StackOverflow community helped me develop a fairly concise @memoize decorator that is

  • 0

Recently, the StackOverflow community helped me develop a fairly concise @memoize decorator that is able to decorate not only functions but also methods and classes in a general way, ie, without having any foreknowledge of what type of thing it will be decorating.

One of the problems that I ran into is that if you decorated a class with @memoize, and then tried to decorate one of it’s methods with @staticmethod, this would not work as expected, ie, you would not be able to call ClassName.thestaticmethod() at all. The original solution that I came up with looked like this:

def memoize(obj):
    """General-purpose cache for classes, methods, and functions."""
    cache = obj.cache = {}

    def memoizer(*args, **kwargs):
        """Do cache lookups and populate the cache in the case of misses."""
        key = args[0] if len(args) is 1 else args
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
        return cache[key]

    # Make the memoizer func masquerade as the object we are memoizing.
    # This makes class attributes and static methods behave as expected.
    for k, v in obj.__dict__.items():
        memoizer.__dict__[k] = v.__func__ if type(v) is staticmethod else v
    return memoizer

But then I learned about functools.wraps, which is intended to make the decorator function masquerade as the decorated function in a much cleaner and more complete way, and indeed I adopted it like this:

def memoize(obj):
    """General-purpose cache for class instantiations, methods, and functions."""
    cache = obj.cache = {}

    @functools.wraps(obj)
    def memoizer(*args, **kwargs):
        """Do cache lookups and populate the cache in the case of misses."""
        key = args[0] if len(args) is 1 else args
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
        return cache[key]
    return memoizer

Although this looks very nice, functools.wraps provides absolutely no support for either staticmethods or classmethods. Eg, if you tried something like this:

@memoize
class Flub:
    def __init__(self, foo):
        """It is an error to have more than one instance per foo."""
        self.foo = foo

    @staticmethod
    def do_for_all():
        """Have some effect on all instances of Flub."""
        for flub in Flub.cache.values():
            print flub.foo
Flub('alpha') is Flub('alpha')  #=> True
Flub('beta') is Flub('beta')    #=> True
Flub.do_for_all()               #=> 'alpha'
                                #   'beta'

This would work with the first implementation of @memoize I listed, but would raise TypeError: 'staticmethod' object is not callable with the second.

I really, really wanted to solve this just using functools.wraps without having to bring back that __dict__ ugliness, so I actually reimplemented my own staticmethod in pure Python, which looked like this:

class staticmethod(object):
    """Make @staticmethods play nice with @memoize."""

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        """Provide the expected behavior inside memoized classes."""
        return self.func(*args, **kwargs)

    def __get__(self, obj, objtype=None):
        """Re-implement the standard behavior for non-memoized classes."""
        return self.func

And this, as far as I can tell, works perfectly alongside the second @memoize implementation that I list above.

So, my question is: Why doesn’t the standard builtin staticmethod behave properly on it’s own, and/or why doesn’t functools.wraps anticipate this situation and solve it for me?

Is this a bug in Python? Or in functools.wraps?

What are the caveats of overriding the builtin staticmethod? Like I say, it seems to be working fine now, but I’m afraid that there might be some hidden incompatibility between my implementation and the builtin implementation, which might blow up later on.

Thanks.

Edit to clarify: In my application, I have a function that does an expensive lookup, and gets called frequently, so I memoized it. That is quite straightforward. In addition to that, I have a number of classes that represent files, and having multiple instances representing the same file in the filesystem will generally result in inconsistent state, so it’s important to enforce only one instance per filename. It’s essentially trivial to adapt the @memoize decorator to this purpose and still retain it’s functionality as a traditional memoizer.

Real world examples of the three different uses of @memoize are here:

  • On a function
  • On a method
  • On a class containing staticmethods
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-06T08:47:10+00:00Added an answer on June 6, 2026 at 8:47 am

    Several thoughts for you:

    • The operation of a staticmethod is completely orthogonal to the operator of class decorators. Making a function into a staticmethod only affect what happens during attribute lookup. A class decorator is a compile-time transformation on a class.

    • There isn’t a “bug” in functools.wraps. All it does is copy function attributes from one function to another.

    • As currently written, your memoize tool does not account for the different call signatures for classmethods and staticmethods. That is a weakness in memoize not in the class tools themselves.

    I think you’ve imagined tools like class decorators, staticmethods, classmethods, and functools to have some sort of mutually integrating intelligence. Instead, all of these tools are very simple and need the programmer to consciously design their interactions.

    ISTM that the underlying problem is that the stated goal is somewhat underspecified: “decorator that is able to decorate not only functions but also methods and classes in a general way, ie, without having any foreknowledge of what type of thing it will be decorating.”

    It is not entirely clear what the semantics of memoize would be in each scenario. And there is no way for Python’s simple components to automatically compose themselves in a way that would be able guess what you really wanted to do.

    My recommendation is that you start with a list of worked out example of memoize using with a variety of objects. Then start building out your current solution to get those to work one at a time. At each step, you will learn where your spec doesn’t match what meoize actually does.

    Another thought is that functools.wraps and class decorators aren’t strictly necessary for this problem. Both can be implemented manually. Start with wiring your tool to do what you want it to do. Once it is working, then look at replacing steps with wraps and decorators. That beats trying to force the tools to your will in situations where they may not be a good fit.

    Hope this helps.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

(Please note that I have seen a similar question on StackOverflow recently, however I
Recently I just got assigned a project to develop a web application/site that uses
Today Recently on Stackoverflow i learned that: reintroduce is used to hide ancestor constructors
I hope I'm not using stackoverflow.com in the wrong way: asking this question! Recently
I came across these questions recently, and could not find the answer on StackOverflow;
Hello StackOverflow community! I started to learn Node.js recently, and decided to implement a
Recently a very friendly user on stackoverflow helped me with this SQL: SELECT (SELECT
Recently I've wanted to do a number of things using CSS only to find
Recently, I asked (and answered) a question on StackOverflow about why a unit test
I've searched all forums recently including StackOverflow, but couldn't find any solution how to

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.