I got a task on courses: Create Fibonacci generator function and create a decorator to leave only even numbers in this sequence. I tried but had no success. Lower I add a code which I try to create.
def decorator(func):
def inner(*args, **kwargs):
print("start decorator")
value = func(*args, **kwargs)
if a%2==0:
print(a)
return value
print("finish decorator")
return inner
#decorator
def fib(n):
a, b = 0, 1
for __ in range(n):
# if a%2==0:
yield a
a, b = b, a + b
print(list(fib(10)))
Please, help to find a way to solve it.
Related
Let's say we have a generator that do operations on a generator, here's an example:
def print_forever(n):
while True:
yield n
def limit_gen(gen, n):
for i in range(n):
yield next(gen)
What's the proper way to insert the values of the generator in a list? list(limit_gen(print_forever(2), 5) for example raises a StopIteration.
In your example the print_forever is instanciated once, then passed to the limit_gen one. After being called once, the first one has yield its one value and reach its own end, raising a StopIteration exception.
There are various ways to make it work, you could for example instanciate a new generator at each iteration:
def print_forever(v):
yield v
def limit_gen(gen, v, n):
for _ in range(n):
yield next(gen(v))
for v in limit_gen(print_forever, 2, 5):
print(v)
Another option is to make a really infinite generator:
def print_forever(v):
while 1:
yield v
def limit_gen(gen, n):
for _ in range(n):
yield next(gen)
for v in limit_gen(print_forever(2), 5):
print(v)
Is it possible to implement a condition in a class iterator with the __next__ method? In a case like the one below, if the condition is not met the iterator returns None, but I'd like to omit these and only receive the actual values. For example:
class Foo2:
def __init__(self, _min=1, _max=5):
self._max = _max
self._min = _min
self.it = -1
def __iter__(self):
return self
def __next__(self):
self.it += 1
if self.it == self._max:
raise StopIteration
if self.it > self._min:
return self.it
fu = Foo2(_min=2, _max=5)
for x in fu:
# if x: -> can this be controlled by the __next__ method in the class?
print(x)
# prints
None
None
None
3
4
I'd like to only print the actual values 3 and 4 but instead of testing for None in the loop, would be nicer to have the class only emit those. Is it possible?
I don't know if this is the most correct approach, as there may be problems/drawbacks that I may be overlooking (would actually appreciate if someone could point these out to me), but for this particular case, if the interest is to only ignore values based on some condition, calling next recursively seems to do the trick:
def __next__(self):
self.it += 1
if self.it == self._max:
raise StopIteration
elif self.it > self._min:
return self.it
else:
return self.__next__()
Works in a for loop and by calling next directly:
fu = Foo2(_min=2, _max=5)
for x in fu:
print(x)
# prints
3
4
fu = Foo2(_min=2, _max=5)
print(next(fu)) # 3
print(next(fu)) # 4
print(next(fu)) # raise StopIteration
I am learning about recursive generators and found this program on web.
I undertand the recursive version of In-Order traversal but i am having trouble understanding recursive generator.
Specifically i am not able to understand
why 'yield x' is written in for loop?
Why 'yield x' is not collected in final list?
I have tried to dubug the generator and added the watch but i found that recursive call execute multiple time 'yield x' and it's not collected in final result.
class Tree:
def __init__(self, label, left=None, right=None):
self.label = label
self.left = left
self.right = right
def __repr__(self, level=0, indent=" "):
s = level*indent + repr(self.label)
if self.left:
s = s + "\\n" + self.left.__repr__(level+1, indent)
if self.right:
s = s + "\\n" + self.right.__repr__(level+1, indent)
return s
def __iter__(self):
return inorder(self)
def tree(list):
n = len(list)
if n == 0:
return []
i = n // 2
return Tree(list[i], tree(list[:i]), tree(list[i + 1:]))
# Recursive Generator
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Role of yield x inside for loop.
Maybe what you don't understand is how the generator works? The generator is different from the iterator, it is not directly calculating the worthy collection, it is dynamically getting all the values. If the result of inorder(t.left) is not traversed by a for loop, then yield inorder(t.left) will return the entire generator created for t.left. Because your entire inorder function is a generator.
Unfortunately, I have not been able to find a specific description document. This is a description based on my experience. Others are welcome to make corrections to my opinion. If someone can provide a specific official description, welcome to add
Not sure exactly what you're looking for, but perhaps it is something along these lines:
class Tree:
def __init__(self, label, left=None, right=None):
self.label = label
self.left = left
self.right = right
def __repr__(self, level=0, indent=" "):
s = level*indent + repr(self.label)
if self.left:
s = s + "\n" + self.left.__repr__(level+1, indent)
if self.right:
s = s + "\n" + self.right.__repr__(level+1, indent)
return s
def __iter__(self):
return inorder(self)
def tree(list):
n = len(list)
if n == 0:
return []
i = n // 2
return Tree(list[i], tree(list[:i]), tree(list[i + 1:]))
def inorder(t):
if t:
for x in t.left:
yield x
yield t.label
for x in t.right:
yield x
t = tree("ABCDEFG")
[print(i.label) for i in t]
which outputs:
A
B
C
D
E
F
G
With that code you could instead:
[print('----------\n', i) for i in t]
which outputs each hierarchical node in the tree, from A to G.
Edit: If you're asking how generator works, perhaps this example could be enlightening:
>>> def list2gen(lst):
... for elem in lst:
... yield str(elem) + '_'
...
>>> print(list2gen([1,2,'7',-4]))
<generator object list2gen at 0x000001B0DEF8B0C0>
>>> print(list(list2gen([1,2,'7',-4])))
['1_', '2_', '7_', '-4_']
If your debugger breaks multiple times at a yield, but those elements never materialize in the resulting generator, you'd have to attribute that to bugs in the debugger. I've not used one for Python in more than a decade; they used to be notoriously bug-infested. In Python the paradigm is "test is king," and "avoid manual debugging," but I disagree with that. (The only reason I don't is lack of great IDE's and debuggers.)
I am trying to pass multiple input entries i as arguments into class Student(). After the last iteration (m), I get a type error. I already tried a for-loop as well, but it didn't work either. Thanks for your help!
class Student():
def __init__(self, d, a, b, c):
self.d = d
self.a = a #name
self.b = b #roll
self.c = c #percentage
return#
def uid(self):
print('UID:', self.d)
def name(self):
print('Name:', self.a)
def roll(self):
print('Roll:', self.b)
def perc(self):
print('Perc:', self.c)
#THIS IS WHAT YOUR INPUT SHOULD LOOK LIKE:
#Peter 405 100
m = input('how many entries? ')
n = 0
while n < int(m):
i = input()
j = i.split()
o = Student(n,*j)
o.uid(), o.name(), o.roll(), o.perc()
n+=1
Student()
The last line - Student() creates an instance of the class without providing any arguments. Hence, Python raises a type error.
Deleting this line (or entering the arguments) will fix the problem.
Trying to multiply all the numbers in a stack, I originally thought of popping all elements into a list and then multiplying but wasn't sure how to/ if that was right.
this is my current code but I'm getting:
TypeError: 'method' object cannot be interpreted as an integer.
def multi_stack(s):
stack = Stack()
mult = 1
size = my_stack.size
for number in range(size):
tmp = my_stack.pop(size)
mult = mult * tmp
L.append(tmp)
for number in range(size):
my_stack.push(L.pop())
print(must)
I made a test case aswell
my_stack = Stack()
my_stack.push(12)
my_stack.push(2)
my_stack.push(4)
my_stack.push(40)
print(multi_stack(my_stack))
print(my_stack.size())`
this should print out :
3840
0
The Stack class I'm using
class Stack():
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self,items):
return self.items.append(items)
def pop(self):
if self.is_empty() == True:
raise IndexError("The stack is empty!")
else:
return self.items.pop()
def peek(self):
if self.is_empty() == True:
raise IndexError("The stack is empty!")
else:
return self.items[len(self.items) - 1]
def size(self):
return len(self.items)
Python lists support append() and pop() methods that allow you to replicate LIFO stack behavior. Use append() to add to the end and pop() to remove the last element.
However, the underlying data structure is still a list. You can use many things to multiply a list together. for example, assuming a non-empty list:
import functools
mylist = [i for i in range(1, 10)]
product = functools.reduce(lambda x, y: x * y, mylist)
or
mylist = [i for i in range(1, 10)]
product = mylist[0]
for j in mylist[1:]:
product *= j
EDIT: Here is an example using your Stack class:
import functools
stack = Stack()
stack.push(1)
stack.push(3)
stack.push(9)
def multi_stack(s):
"""
s: a Stack() object
"""
return functools.reduce(lambda x, y: x * y, s.items)
def multi_stack_readable(s):
"""
s: a Stack() object
"""
if s.size() > 1:
product = s.items[0]
for i in s.items[1:]:
product *= i
return product
elif s.size() == 1:
return s.items
else:
raise IndexError("the stack is empty!")
print(multi_stack(stack))
print(multi_stack_readable(stack))
Using lambda functions is sometimes considered less readable, so I included a more readable version using a for loop. Both produce the same result.
Your code doesnt work because size = my_stack.size returns a method object and not the integer you expected; you forgot to add the parentheses at the end to actually call the method. So when you tried for number in range(size):, you get an exception because you are passing a method object instead of an integer to range(). There are also a bunch of other mistakes: you didnt use the parameter passed to the function at all, instead affecting global variable my_stack (unless that was your intent); you're performing operations on some unknown variable L; you created stack at the top of your function and did nothing with it, and so on. In general, too convoluted for such a simple goal. There are more efficient ways to do this but correcting your code:
def multi_stack(s):
mult = 1
size = s.size()
for i in range(size):
tmp = s.pop()
mult = mult * tmp
return mult
This should return your expected product, though it wont empty the stack. If you want to do that, then get rid of the function parameter, and substitute s for my_stack as before.