why for loop is used to generate numbers through function? - python-3.x

I can't get why for i in gen(100): print(i) is being used here. when i replace print(i) with print(gen(i)) it start giving memory location. I do know that yield is used for one time storage but how exactly is it working?
def gen(num):
i = 0
while i<num:
x=i
i+=1
if x%7 == 0:
yield x
for i in gen(100):
print(i)

yield is not used for one-time storage. yield makes a function return a generator
A generator is an iterable object (which means you can use it in place of any sequences such as list(gen()), for i in gen(), etc.). You can also pass it to the next() built-in function that advances a generator one step forward (makes it begin or start where it left off and run to the first yield it hits). It also returns the yielded value
def gen():
for i in range(5):
yield i
print(list(gen())) # prints [0, 1, 2, 3, 4]
print(next(gen())) # prints 0
gn = gen()
print(next(gn)) # prints 0
print(list(gn)) # prints [1, 2, 3, 4]
print(next(gn)) # raises StopIteration, because the generator is
# exhausted (the generator function ran to completion)
The reason why you're getting a memory address from print(gen(i)) is because you're actually printing a generator object, not the value it produces. So that's why generators first have to be iterated somehow

Related

Can I use side_effect in my mocking to provide an indefinite number of values?

So I can use an iterable with side_effect in python mock to produce changing values returned by my calls to the mock:
some_mock.side_effect = [1, 2, 3]
return_value provides the same value every time
some_mock.return_value = 8
Is there a way I can use one or both of these methods so that a mock produces some scheduled values to begin and then an infinite response of one particular value when the first set is exhausted? i.e.:
[1, 2, 3, 8, 8, 8, 8, 8, 8, etc. etc etc.]
There is no specific build-in feature that does that, but you can achieve this by adding a side effect that does this.
In the cases I can think of, it would be sufficient to just add some highest needed number of values instead of an infinite number, and use the side_effect version that takes a list:
side_effect = [1, 2, 3] + [8] * 100
my_mock.side_effect = side_effect
If you really need that infinite number of responses, you can use the other version of side_effect instead that uses a function object instead of a list. You need some generator function that creates your infinite list, and iterate over that in your function, remembering the current position. Here is an example implementation for that (using a class to avoid global variables):
from itertools import repeat
class SideEffect:
def __init__(self):
self.it = self.generator() # holds the current iterator
#staticmethod
def generator():
yield from range(1, 4) # yields 1, 2, 3
yield from repeat(8) # yields 8 infinitely
def side_effect(self, *args, **kwargs):
return next(self.it)
...
my_mock.side_effect = SideEffect().side_effect
This should have the wanted effect.

python3 holding on to data after recursion

I wrote some code that found the fastest way of getting the sum of a number by adding numbers that were part of a list together ex:
bestSum(3, [800, 1, 3]) would return [3] because that would be the best way of getting 3 (first number) with the numbers provided would be simply adding 3. Code:
def bestSum(target, lst, mochi = {}):
if target in mochi:
return mochi[target]
if target == 0:
return []
if target < 0:
return None
shortestCombination = None
for i in lst:
remainderCombination = bestSum(target - i, lst, mochi)
if remainderCombination is not None:
remainderCombination = [*remainderCombination, i]
if shortestCombination is None or len(remainderCombination) < len(shortestCombination):
shortestCombination = remainderCombination
mochi[target] = shortestCombination
return shortestCombination
I ran into this issue where data would be saved between times I ran the code, for example if I run just
print(bestSum(8, [4])
It Returns
[4, 4]
However if I run
print(bestSum(8, [2, 3, 5]))
print(bestSum(8, [4]))
it returns:
[5, 3]
[5, 3]
Am I doing something wrong here? Is this potentially a security vulnerability? Is there a way to make this return correctly? What would cause python to do something like this?
This is documented behavior when using mutables as default arguments (see "Default parameter values are evaluated from left to right when the function definition is executed.").
As discussed in the documentation, "A way around this is to use None as the default, and explicitly test for it in the body of the function".
[While documented, I only learned about it here on SO a couple of days ago]

Using Recursion to get lists of original list in Python returns error

I am given a task to use recursion so that any list that is given to it say p would be return in the desired output as shown at the end without disturbing or altering the contents of p. When I run this function it says that local variable (y) is referenced without assignment i.e. len(y)==0 but I have already assigned y outside the function init. Can someone help identify what's wrong?
y=[] #To copy contents of x in it so that x is not modified
z=[] # To return it as a list required in the question
p=[1,2,3]
def inits(x):
if len(y)==0 and len(z)==0:
y=[i.copy for i in x] #Copying contents of x into x so that it remains unchanged
if len(z)==len(x)+1: #z list is one plus x list because it has [] in it
print(z)
else:
z.append(y.copy())
if len(y)!=0: #This is done so that when y=[] it does not return error for y.pop(-1)
y.pop(-1)
inits(x)
inits(p)
#Desired Output
[[1,2,3],[1,2],[1],[]]
If you don't need the last empty element then feel free to remove the else block.
Your y variable is also not needed as for me
z = [] # To return it as a list required in the question
def foo(param):
if len(param):
z.append([i for i in param])
foo(param[0:-1])
else:
z.append([])
foo([1, 2, 3])
print(z)
[[1, 2, 3], [1, 2], [1], []]

Return terminates for loop

I am trying to define a following function that should return all odd numbers between two given numbers, however the return terminates the for loop such that only the first number is returned. How do I overcome this?
def oddNumbers(l,r):
L=[]
for i in range (l,r+1):
L.append(i)
for i in range (len(L)):
if L[i] % 2 !=0:
return(L[i])
else:
continue
Since you're starting with Python, I recommend you to read PEP 8 in order to know more about the style guide for Python in general. Hope you enjoy your Python journey!
Regarding your function, as you mentioned, Python terminates the execution of a program when a return is found. It's not a basic concept, but Python has something called generators (you can check more here) that you can use to easy fix your function:
def odd_numbers(l, r):
for i in range (l, r + 1):
if i % 2 == 0:
continue
else:
yield i
See that I simplified the function a bit (just one for loop is necessary). The most important part is the change from return to yield. That's the part where you create a generator. It's basically a "lazy" (memory efficient) return that doesn't terminate the execution.
if generators is too difficult for you, you can try this.
code:
def odd_numbers(l,r):
ans = []
for i in range (l,r+1):
if i % 2 == 1:
ans.append(i)
return ans
print(odd_numbers(1,10))
result:
[1, 3, 5, 7, 9]
A more pythonic to do it by using list comprehension
code:
def odd_numbers_list_comprehension(l,r):
return [i for i in range (l,r+1) if i % 2 == 1]
print(odd_numbers_list_comprehension(1,10))
result:
[1, 3, 5, 7, 9]

Is the rear item in a Queue the last item added or the item at the end of a Queue?

My professor wrote a Queue class that uses arrays. I was giving it multiple test cases and got confused with one specific part. I want to figure out if the last item added is the rear of the queue. Lets say I enqueued 8 elements:
[1, 2, 3, 4, 5, 6, 7, 8]
Then I dequeued. And now:
[None, 2, 3, 4, 5, 6, 7, 8]
I enqueued 9 onto the Queue and it goes to the front. However, when I called my method that returns the rear item of the queue, q.que_rear, it returned 8. I thought the rear item would be 9? Since it was the last item added.
Here is how I tested it in case anyone is confused:
>>> q = ArrayQueue()
>>> q.enqueue(1)
>>> q.enqueue(2)
>>> q.enqueue(3)
>>> q.enqueue(4)
>>> q.data
[1, 2, 3, 4, None, None, None, None]
>>> q.dequeue()
1
>>> q.enqueue(5)
>>> q.enqueue(6)
>>> q.enqueue(7)
>>> q.enqueue(8)
>>> q.data
[None, 2, 3, 4, 5, 6, 7, 8]
>>> q.enqueue(9)
>>> q.data
[9, 2, 3, 4, 5, 6, 7, 8]
>>> q.que_rear()
Rear item is 8
EDIT
I just want to know what’s supposed to be the “rear of the Queue”? The last element added, or the element at the end of the list? In this case I showed, is it supposed to be 8 or 9?
Here is my code:
class ArrayQueue:
INITIAL_CAPACITY = 8
def __init__(self):
self.data = [None] * ArrayQueue.INITIAL_CAPACITY
self.rear = ArrayQueue.INITIAL_CAPACITY -1
self.num_of_elems = 0
self.front_ind = None
# O(1) time
def __len__(self):
return self.num_of_elems
# O(1) time
def is_empty(self):
return len(self) == 0
# Amortized worst case running time is O(1)
def enqueue(self, elem):
if self.num_of_elems == len(self.data):
self.resize(2 * len(self.data))
if self.is_empty():
self.data[0] = elem
self.front_ind = 0
self.num_of_elems += 1
else:
back_ind = (self.front_ind + self.num_of_elems) % len(self.data)
self.data[back_ind] = elem
self.num_of_elems += 1
def dequeue(self):
if self.is_empty():
raise Exception("Queue is empty")
elem = self.data[self.front_ind]
self.data[self.front_ind] = None
self.front_ind = (self.front_ind + 1) % len(self.data)
self.num_of_elems -= 1
if self.is_empty():
self.front_ind = None
# As with dynamic arrays, we shrink the underlying array (by half) if we are using less than 1/4 of the capacity
elif len(self) < len(self.data) // 4:
self.resize(len(self.data) // 2)
return elem
# O(1) running time
def first(self):
if self.is_empty():
raise Exception("Queue is empty")
return self.data[self.front_ind]
def que_rear(self):
if self.is_empty():
print("Queue is empty")
print("Rear item is", self.data[self.rear])
# Resizing takes time O(n) where n is the number of elements in the queue
def resize(self, new_capacity):
old_data = self.data
self.data = [None] * new_capacity
old_ind = self.front_ind
for new_ind in range(self.num_of_elems):
self.data[new_ind] = old_data[old_ind]
old_ind = (old_ind + 1) % len(old_data)
self.front_ind = 0
The que_rear function seems to be added post-hoc in an attempt to understand how the internal circular queue operates. But notice that self.rear (the variable que_rear uses to determine what the "rear" is) is a meaningless garbage variable, in spite of its promising name. In the initializer, it's set to the internal array length and never gets touched again, so it's just pure luck if it prints out the rear or anything remotely related to the rear.
The true rear is actually the variable back_ind, which is computed on the spot whenever enqueue is called, which is the only time it matters what the back is. Typically, queue data structures don't permit access to the back or rear (if it did, that would make it a deque, or double-ended queue), so all of this is irrelevant and implementation-specific from the perspective of the client (the code which is using the class to do a task as a black box, without caring how it works).
Here's a function that gives you the actual rear. Unsurprisingly, it's pretty much a copy of part of enqueue:
def queue_rear(self):
if self.is_empty():
raise Exception("Queue is empty")
back_ind = (self.front_ind + self.num_of_elems - 1) % len(self.data)
return self.data[back_ind]
Also, I understand this class is likely for educational purposes, but I'm obliged to mention that in a real application, use collections.dequeue for all your queueing needs (unless you need a synchronized queue).
Interestingly, CPython doesn't use a circular array to implement the deque, but Java does in its ArrayDeque class, which is worth a read.

Resources