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

  • SEARCH
  • Home
  • 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 8060173
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 5, 20262026-06-05T09:54:21+00:00 2026-06-05T09:54:21+00:00

So I recently asked a question about memoization and got some great answers, and

  • 0

So I recently asked a question about memoization and got some great answers, and now I want to take it to the next level. After quite a bit of googling, I could not find a reference implementation of a memoize decorator that was able to cache a function that took keyword arguments. In fact, most of them simply used *args as the key for cache lookups, meaning that it would also break if you wanted to memoize a function that accepted lists or dicts as arguments.

In my case, the first argument to the function is a unique identifier by itself, suitable for use as a dict key for cache lookups, however I wanted the ability to use keyword arguments and still access the same cache. What I mean is, my_func('unique_id', 10) and my_func(foo=10, func_id='unique_id') should both return the same cached result.

In order to do this, what we need is a clean and pythonic way of saying ‘inspect kwargs for whichever keyword it is that corresponds to the first argument)’. This is what I came up with:

class memoize(object):
    def __init__(self, cls):
        if type(cls) is FunctionType:
            # Let's just pretend that the function you gave us is a class.
            cls.instances = {}
            cls.__init__ = cls
        self.cls = cls
        self.__dict__.update(cls.__dict__)

    def __call__(self, *args, **kwargs):
        """Return a cached instance of the appropriate class if it exists."""
        # This is some dark magic we're using here, but it's how we discover
        # that the first argument to Photograph.__init__ is 'filename', but the
        # first argument to Camera.__init__ is 'camera_id' in a general way.
        delta = 2 if type(self.cls) is FunctionType else 1
        first_keyword_arg = [k
            for k, v in inspect.getcallargs(
                self.cls.__init__,
                'self',
                'first argument',
                *['subsequent args'] * (len(args) + len(kwargs) - delta)).items()
                    if v == 'first argument'][0]
        key = kwargs.get(first_keyword_arg) or args[0]
        print key
        if key not in self.cls.instances:
            self.cls.instances[key] = self.cls(*args, **kwargs)
        return self.cls.instances[key]

The crazy thing is that this actually works. Eg, if you decorate like this:

@memoize
class FooBar:
    instances = {}

    def __init__(self, unique_id, irrelevant=None):
        print id(self)

Then from your code you can call either FooBar('12345', 20) or FooBar(irrelevant=20, unique_id='12345') and actually get the same instance of FooBar. You can then define a different class with a different name for the first argument, because it works in a general way (ie, the decorator doesn’t need to know anything specfic about the class it’s decorating in order for this to work).

The problem is, it’s an ungodly mess 😉

It works because inspect.getcallargs returns a dict mapping the defined keywords to the arguments you supply it, so I supply it some phony arguments and then inspect the dict for the first argument that got passed.

What would be much nicer, if such a thing were to even exist, is an analogue to inspect.getcallargs that returned both kinds of arguments unified as a list of the arguments instead of as a dict of the keyword arguments. That would allow something like this:

def __call__(self, *args, **kwargs):
    key = inspect.getcallargsaslist(self.cls.__init__, None, *args, **kwargs)[1]
    if key not in self.cls.instances:
        self.cls.instances[key] = self.cls(*args, **kwargs)
    return self.cls.instances[key]

The other way I can see of tackling this would be using the dict provided by inspect.getcallargs as the lookup cache key directly, but that would require a repeatable way to make identical strings from identical hashes, which is something I’ve heard can’t be relied upon (I guess i’d have to construct the string myself after sorting the keys).

Does anybody have any thoughts on this? Is it Wrong to want to call a function with keyword arguments and cache the results? Or just Very Difficult?

  • 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-05T09:54:24+00:00Added an answer on June 5, 2026 at 9:54 am

    I’d suggest something like the following:

    import inspect
    
    class key_memoized(object):
        def __init__(self, func):
           self.func = func
           self.cache = {}
    
        def __call__(self, *args, **kwargs):
            key = self.key(args, kwargs)
            if key not in self.cache:
                self.cache[key] = self.func(*args, **kwargs)
            return self.cache[key]
    
        def normalize_args(self, args, kwargs):
            spec = inspect.getargs(self.func.__code__).args
            return dict(kwargs.items() + zip(spec, args))
    
        def key(self, args, kwargs):
            a = self.normalize_args(args, kwargs)
            return tuple(sorted(a.items()))
    

    Example:

    @key_memoized
    def foo(bar, baz, spam):
        print 'calling foo: bar=%r baz=%r spam=%r' % (bar, baz, spam)
        return bar + baz + spam
    
    print foo(1, 2, 3)
    print foo(1, 2, spam=3)         #memoized
    print foo(spam=3, baz=2, bar=1) #memoized
    

    Note that you can also extend key_memoized and override its key() method to provide more specific memoization strategies, e.g. to ignore some of the arguments:

    class memoize_by_bar(key_memoized):
        def key(self, args, kwargs):
            return self.normalize_args(args, kwargs)['bar']
    
    @memoize_by_bar
    def foo(bar, baz, spam):
        print 'calling foo: bar=%r baz=%r spam=%r' % (bar, baz, spam)
        return bar
    
    print foo('x', 'ignore1', 'ignore2')
    print foo('x', 'ignore3', 'ignore4')
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I recently asked a question about Self-Joins and I got a great answer. The
I recently asked a question about structs, and optimizing some overloaded operators. The original
I recently asked a question about some problems I was having getting MIT Kerberos
Quite recently, I asked a question about debugging a seg fault: What are some
I recently asked a question about functional programming, and received (good!) answers that prompted
I recently asked a question about committing a new file but now I am
I recently asked this question , but after some of the responses and some
I recently asked a question about formatting JavaScript code in Vim. And I've also
I've recently asked a question about clipping an image via path at view's drawRect
Recently I have asked a question about what I should use to create self-contained

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.