Why does this create a StopIteration error? [duplicate] - python

This question already has answers here:
How to clone a Python generator object?
(6 answers)
Closed 2 years ago.
Why does this
a = (i for i in range(2))
b = a
c = a
for i in b:
print("ok")
next(c)
result in this?
StopIteration Traceback (most recent call last)
<ipython-input-37-9c481bb09894> in <module>()
54 for i in b:
55 print("ok")
---> 56 next(c)
StopIteration:
I'm currently learning about generators in python. My goal here was to set up a as a generator, make b, and c instances of a and use b and c separately. What went wrong?
Additionally, everything went well when I set up something similar with a function using yield instead of the () based generator a.

BOTH b and c are pointing to same generator object , while you are looping through generator b , it is consuming elements of c also , so once you consume all elements of b,
and trying to consume it with c i.e by next(c) it is giving you StopIteration error.

Related

Get only one of a function's returned values [duplicate]

This question already has answers here:
Ignore python multiple return value
(12 answers)
Closed 1 year ago.
I have defined a function as:
def f():
a = 5
b = 6
c = 7
def g(x):
return x+2
return a, b , c, g
I would like to know how to get only one of the value returned, without the other ones. For example If I am only interested in c, is there an alternative to:
a, b, c, g = f()
To get c?
Python returns multiple values as a tuple. You can check this by doing
>>> type(f())
>>> <class 'tuple'>
As from your implementation we can assume that the c variable value always lies on the index 2 so you can do
c_value = f()[2]

TypeError: 'int' object is not subscriptable in python dictionary

def list_gen(a,b,c,d):
print(a,b,c,d)
l=[]
for i in range(a,b):
for j in range(c,d):
l.append(d[i,j])
return l
When I pass the arguments to the function list_gen(0,3,0,3) I am getting below error:
TypeError Traceback (most recent call last)
<ipython-input-51-ac343943d448> in <module>()
----> 1 list_gen(0,3,0,3)
<ipython-input-49-afc3d3a347a9> in list_gen(a, b, c, d)
4 for i in range(a,b):
5 for j in range(c,d):
----> 6 l.append(d[i,j])
7 return l
TypeError: 'int' object is not subscriptable
But this code works without any issues. Can anyone tell what is the error here ?
for i in range(0,3):
for j in range(0,3):
print(d[i,j])
Apparently you have a global variable d containing a dict that you want to access. But you used d as the name of a parameter of the function, so that hides the global name. When you use d[i,j], it tries to index the number 3, which is not possible.
The solution is to use a different variable for the function parameter.
def list_gen(a,b,c,e):
print(a,b,c,e)
l=[]
for i in range(a,b):
for j in range(c,e):
l.append(d[i,j])
return l
You'll run into problems like this less often if you use more verbose, meaningful variable names than d.
The parameter in range has to be integer and since the for j in range(c,d): didn't throw error, it mean d is int and you cannot reference int using d[i,j] in the line l.append(d[i,j]) . You probably have a variable named d whose scope is active during the print statement.

In Python, why do function parameters maintain their value beetween function calls [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 5 years ago.
I'm fairly new at Python and i cannot wrap my head around the results im getting
Using the code below:
def func(a,b=set()):
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
print(func([1,1,2,2,3,4]))
print(func([1,1,2,2,3,4]))
I was getting output:
[1,2,3,4]
[]
I put "print(b)" above "res=list()" and got output:
set()
[1,2,3,4]
{1,2,3,4}
[]
What is going on? Shouldn't "b" be set to "set()" when i call the function? Im using Python 3.6
Have a look at the documentation for default parameters:
The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes.
When you define a function with a default parameter, the default value is only evaluated when the definition is first executed by the interpreter (the actual def statement). This is usually not a problem, except for when a mutable default value is used. That is to say, one that can be modified in place.
In your case, when you modify b in your function the first time you call it, it keeps that value the next time around. To avoid this, you can do like so:
def func(a,b=None):
if b is None:
b = set()
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
Now b will always have the default value you want.
In python functions are objects and arguments are evaluated and executed once. this is a nice explanation : http://effbot.org/zone/default-values.htm
in your example, it could be "fixed" by doing:
def func(a,b=None):
if b is None:
b = set()
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
In First function call b is empty
See here
In second function call b is already fill with element
So this condition not run if i not in b and return empty list
Try this
def func(a):
res=list()
b=set()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
print(func([1,1,2,2,3,4]))
print(func([1,1,2,2,3,4]))
Output
[1, 2, 3, 4]
[1, 2, 3, 4]

Exact return point of yield in python generators

In a quest to teach myself Python generators, especially their send() method, I wanted to write a toy example that would allow me calculate an "interruptible" Fibonacci sequence.
Eventually I succeeded, but I don't understand exactly what is going on. Consider these two examples.
Successful example:
def genFib(a=0, b=1):
while True:
c = yield a+b
if c:
a, b = b, c
else:
a, b = b, a+b
return
​
fib_number = genFib()
​
print fib_number.next()
print fib_number.next()
print fib_number.next()
print fib_number.next()
print fib_number.next()
print fib_number.send(100)
print fib_number.next()
print fib_number.next()
​
1
2
3
5
8
105
205
310
Failure that I thought would work:
def genFib(a=0, b=1):
while True:
c = yield a+b
a, b = b, c
return
​
fib_number = genFib()
​
print fib_number.next()
print fib_number.next()
1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-59-4513153ea517> in <module>()
8
9 print fib_number.next()
---> 10 print fib_number.next()
<ipython-input-59-4513153ea517> in genFib(a, b)
1 def genFib(a=0, b=1):
2 while True:
----> 3 c = yield a+b
4 a, b = b, c
5 return
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
Why does the second example not work? In particular, the second example seems assign a value of None to c in all but the first executions of the generator. Why? How does it know to assign to c the first time next() is called, but not the next next() time?
My understanding was that generators resume execution (when they are called again) beginning on the line after the yield line, but my example has me thinking this is wrong, at least in some cases. Where exactly does execution resume from? Does it re-execute the yield line but this time with somehow leaving off everything after the yield? How does c get assigned to None?
yield is a bit of a strange expression. Execution pauses at the yield after the right-hand side is evaluated, but before the assignment takes place.
The value that is returned in the yield statement is whatever you .send to the generator. Since you are calling .next rather than .send, the value returned from the yield expression (c) is None. In the first example, that's fine since c is checked for truthiness and then is not used if it is falsy (e.g. None). However, in the second example, c isn't checked so you end up adding None to an integer (which obviously doesn't produce desirable results).

Bug or misunderstanding in Python 2.7? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
Am I missing something here or is this really a bug? Below is a recursive function that generates a multi-dimensional list from a tuple specification, for example.
dim((2,3))
returns
[[[],[],[]],[[],[],[]]]
The only problem is that it adds to the list each time I call it, if I call it without the default parameter, if I specify the default parameter like dim((2,3),[]), then it's fine. It's saving the state of the default parameter each call! If no one can find a problem with what I'm doing I'll enter it in the python bug reporter.
cdr = lambda l : l[1:]
car = lambda l : l[0]
last = lambda x : x[-1:][0]
def dim(t, c = []):
if len(t) > 0:
i = car(t)
for j in range(i):
c.append([])
dim(cdr(t), last(c))
return c
print dim([2,3])
print dim([2,3])
print dim([2,3])
print dim([2,3])
def dim(t, c = [])
It's a bug (in your code). That c = [] part is only evaluated once during the entire program. When you call dim, c is being continuously appended to. A better approach would be this:
def dim(t, c=None):
if c is None:
c = []
...

Categories