简体   繁体   中英

Python: Generators and decorators with for a dictionary

I created a problem that exercises a one's ability to use generators, decorators, and dictionaries in Python.

However, I cannot solve this exercise myself, and was wondering if it is at all possible to solve.

Is it possible to cache function output in the form of a dictionary using a decorator function that wraps a generator?

The exercise is:

Write a decorator to cache function invocation results. Store pairs arg:result in a dictionary in an attribute of the function object. Generate these results using a generator function Test your code on the fibonacci function.

I have attempted to implement it as follows:

def cachefunc(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return {func.__name__ + '(' + str(list(args))[1:-1] + ')' : str(result)}
    wrapper.__name__ = func.__name__
    wrapper.__doc__ = func.__doc__
    return wrapper

@cachefunc
def fibonacci(n):
    assert n >= 0
    if n < 2:
        return n
    else:
        return (fibonacci(n-1) + fibonacci(n-2))
def allfib():
    n = 0
    while True:
        yield fibonacci(n)
        n += 1

result = []
generator = allfib()
while len(result) < 10:
    x = next(generator)
    result.append(x)
print result

However, I get the following error:

python dg.py
Traceback (most recent call last):
  File "dg.py", line 32, in <module>
    x = next(generator)
  File "dg.py", line 26, in allfib
    yield fibonacci(n)
  File "dg.py", line 10, in wrapper
    result = func(*args, **kwargs)
  File "dg.py", line 22, in fibonacci
    return (fibonacci(n-1) + fibonacci(n-2))
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

Does anybody know an alternative solution to a question like this?

Your Specific Error

The error is here:

return {func.__name__ + '(' + str(list(args))[1:-1] + ')' : str(result)}

You are returning a dictionary when you want to return result I believe. I don't quite see where you are doing any cacheing actually inside cache function.

General Comments

This pattern of cacheing is also commonly called memoization.

Try this http://avinashv.net/2008/04/python-decorators-syntactic-sugar/ . Scroll down to the portion about fibonacci. The following code is found there:

class memoize:
  def __init__(self, function):
    self.function = function
    self.func_name = function.__name__
    self.memoized = {}

  def __call__(self, *args):
    try:
      print "Using Memo Solution for " + self.func_name + " on " + str(args)
      return self.memoized[args]
    except KeyError:
      print "Computing Solution Now for " + self.func_name + " on " + str(args)
      self.memoized[args] = self.function(*args)
    return self.memoized[args]

Then simply do:

@memoize
def fibonacci(n):
  assert n >= 0
  if n < 2:
    return n
  else:
    return (fibonacci(n-1) + fibonacci(n-2))

Full Code

Memoization still works on the generator in your example, note the print statements show you that memoized results are being fetched.

class memoize:
  def __init__(self, function):
    self.function = function
    self.func_name = function.__name__
    self.memoized = {}

  def __call__(self, *args):
    try:
      print "Using Memo Solution for " + self.func_name + " on " + str(args)
      return self.memoized[args]
    except KeyError:
      print "Computing Solution Now for " + self.func_name + " on " + str(args)
      self.memoized[args] = self.function(*args)
    return self.memoized[args]

@memoize
def fibonacci(n):
  assert n >= 0
  if n < 2:
    return n
  else:
    return (fibonacci(n-1) + fibonacci(n-2))

def allfib():
  n = 0
  while True:
    yield fibonacci(n)
    n += 1

result = []
generator = allfib()
while len(result) < 10:
  x = next(generator)
  result.append(x)
print result

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM