I am writing some code that traverses a structure that may have cyclic references. Rather than explicitly doing checks at the beginning of the recursive functions I thought that I would create a decorator that didn't allow a function to be called more than once with the same arguments.
Below is what I came up with. As it is written, this will try to iterate over Nonetype and raise an exception. I know that I could fix it by returning say an empty list, but I wanted to be more elegant. Is there a way to tell from within the decorator whether the function being decorated is a generator function or not? This way I could conditionally raise StopIteration if it is a generator or just return None otherwise.
previous = set()
def NO_DUPLICATE_CALLS(func):
def wrapped(*args, **kwargs):
if args in previous:
print 'skipping previous call to %s with args %s %s' % (func.func_name, repr(args), repr(kwargs))
return
else:
ret = func(*args, **kwargs)
previous.add(args)
return ret
return wrapped
@NO_DUPLICATE_CALLS
def foo(x):
for y in x:
yield y
for f in foo('Hello'):
print f
for f in foo('Hello'):
print f
Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python. Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).
It is quite simple to create a generator in Python. It is similar to the normal function defined by the def keyword and uses a yield keyword instead of return. Or we can say that if the body of any function contains a yield statement, it automatically becomes a generator function.
It's not thread-safe; simultaneous calls may interleave, and mess with the local variables.
Generators allow you to create iterators in a very pythonic manner. Iterators allow lazy evaluation, only generating the next element of an iterable object when requested. This is useful for very large data sets. Iterators and generators can only be iterated over once.
Okay, check this out:
>>> from inspect import isgeneratorfunction
>>> def foo(x):
... for y in x:
... yield y
...
>>> isgeneratorfunction(foo)
True
This requires Python 2.6 or higher, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With